Spring-IOC专题

Spring-IOC专题

为什么引入IOC?

1
2
3
4
5
6
class Programer {
Computer computer = new Mac2015();
private void work() {
computer.help();
}
}

此时有一个问题就是computer和programer耦合在一起,这个programer不具备扩展性(它只会用mac2015),如果此时公司换了一批电脑Mac2016,那么需要重新创建一个新的程序员类,这显然是不合理的。
从设计的角度来讲,类本身就是定义的一个模板亦或是一种抽象,如果抽象和具体的实现绑定或者耦合在一起,那么就不是纯粹的抽象了。
这也违背了设计模式中的don’t call me法则。所以这个时候要把computer和programer解耦,解耦的方法很简单。
computer的具体实现由调用方指定就ok,而不是在类内部自行指定。那么类就需要暴露一些接口供外界实现注入功能。

常见的方法有三种

  • 构造函数注入
  • set注入
  • 接口注入

这就是IOC的基本思路,而对于我们web开发来讲一般不存在调用方(其实也有不过在容器层面),所以如何保证类中属性的动态注入,这个就要由Spring登场了

接下来,选择何种注入方式以及需要注入哪些属性是怎么通知到Spring的呢?
常见的方法有两种

  • 注解
  • 配置文件(xml/properties)

这里多提一句,bean的实例化方式可以是普通的构造器构造也可以是工厂方法构造
下面这个是通过静态工厂注入的方式

1
2
3
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>

class指向的是包含工厂方法的类,并非工厂方法返回的类
下面这个是通过普通工厂注入的方式

1
2
3
4
5
6
7
8
9
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>

IOC容器分类

  • BeanFactory

    默认lazy-load,所以启动较快
    常用的一个实现类是DefaultListableBeanFactory,该类还实现了BeanDefinitionRegistry接口,该接口担当Bean注册管理的角色 registerBeanDefinition#BeanDefinitionRegistry
    bean会以beanDefinition的形式注册在BeanDefinitionRegistry中
    BeanFactory接口中只定义了一些关于bean的查询方法,而真正的工作需要其子类实现的其他接口定义~

  • ApplicationContext

    构建于BeanFactory基础之上,非lazy,启动时间较长,除了BeanFactory的基础功能还提供了一些额外的功能(事件发布、国际化信息支持等)

    手动与自动

  • 基于配置文件的注入 我们称他为手动

    <bean> <name=”propertyName” ref=”otherBean”>

  • 基于注解的注入 我们称他为全自动

    @Conponet/@Autowire/@Resource + <componet-scan>

  • 还有注解+配置的半自动

    <bean> + @autowire/@resource

关于xml中的bean

一般最顶层是beans标签 包含0or1个description 多个bean、import、alias
beans标签有几个独特的属性,对包含的所有bean生效

  • default-lazy-init

    所有bean是否懒加载

  • default-autowire

    所有bean内部注入的方式no(默认)byName/byType/constrctor/autodetect这里byName/byType ≈ <bean autowire=byName/byType> ≈ @resource/@autowire

  • default-dependency-check

    是否进行依赖检查 none(默认)

  • default-init-method

    所有bean的初始化方法比如init

  • default-destroy-method

    所有bean的销毁方法比如destroy

基本上beans的所有属性都可以对应到bean中,这里提一下bean的scope属性

关于Singleton和Prototype

singleton类型的bean的存活时间和容器一样长
prototype类型的bean的生命周期不由容器维护,容器不再拥有当前对象的引用,任由其自生自灭

想起当初解决的一个问题,持有对象的引用(new/reflect),但是这个对象未进行注入,如何利用Spring对其进行注入?

1
2
applicationContext.getAutowireCapableBeanFactory().
autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_NO, true);

Bean的生命周期

先放结论,后面有具体的代码论证(图文结合效果更佳)

实践是检验真理的唯一标准,举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class Car implements BeanFactoryAware, BeanNameAware,
InitializingBean, DisposableBean {
private String carName;
private BeanFactory beanFactory;
private String beanName;

public Car() {
System.out.println("bean的构造函数");
}

public void setCarName(String carName) {
System.out.println("属性注入");
this.carName = carName;
}

// 这是BeanFactoryAware接口方法
public void setBeanFactory(BeanFactory arg0) throws BeansException {
System.out
.println("BeanFactoryAware.setBeanFactory()");
this.beanFactory = arg0;
}

// 这是BeanNameAware接口方法
public void setBeanName(String arg0) {
System.out.println("BeanNameAware.setBeanName()");
this.beanName = arg0;
}

// 这是InitializingBean接口方法
public void afterPropertiesSet() throws Exception {
System.out
.println("InitializingBean.afterPropertiesSet()");
}

// 这是DiposibleBean接口方法
public void destroy() throws Exception {
System.out.println("DiposibleBean.destory()");
}

// 通过<bean>的init-method属性指定的初始化方法
public void myInit() {
System.out.println("<bean>的init-method属性指定的初始化方法");
}

// 通过<bean>的destroy-method属性指定的初始化方法
public void myDestory() {
System.out.println("<bean>的destroy-method属性指定的初始化方法");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MyBeanPostProcessor implements BeanPostProcessor {

public MyBeanPostProcessor() {
super();
System.out.println("BeanPostProcessor构造函数");
}

public Object postProcessAfterInitialization(Object arg0, String arg1)
throws BeansException {
if (arg1.equals("car")) {
System.out
.println("BeanPostProcessor.postProcessAfterInitialization()");
}
return arg0;
}

public Object postProcessBeforeInitialization(Object arg0, String arg1)
throws BeansException {
if (arg1.equals("car")) {
System.out
.println("BeanPostProcessor.postProcessBeforeInitialization()");
}
return arg0;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

public MyBeanFactoryPostProcessor() {
super();
System.out.println("BeanFactoryPostProcessor构造函数");
}

public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0)
throws BeansException {
System.out.println("BeanFactoryPostProcessor.postProcessBeanFactory()");
System.out.println("我现在可以修改beanDefination但是我这里就不修改了");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class MyInstantiationAwareBeanPostProcessor extends
InstantiationAwareBeanPostProcessorAdapter {

public MyInstantiationAwareBeanPostProcessor() {
super();
System.out
.println("InstantiationAwareBeanPostProcessorAdapter构造函数");
}

// 接口方法、实例化Bean之前调用
@Override
public Object postProcessBeforeInstantiation(Class beanClass,
String beanName) throws BeansException {
if(beanName.equals("car")) {
System.out
.println("InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()");
}
return null;
}

// 接口方法、实例化Bean之后调用
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if(beanName.equals("car")) {
System.out
.println("InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()");
}
return true;
}

// 接口方法、设置某个属性时调用
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs,
PropertyDescriptor[] pds, Object bean, String beanName)
throws BeansException {
if(beanName.equals("car")) {
System.out
.println("InstantiationAwareBeanPostProcessor.postProcessPropertyValues()");
}
return pvs;
}
}
1
2
3
4
5
<bean id="beanPostProcessor" class="test.MyBeanPostProcessor"/>
<bean id="instantiationAwareBeanPostProcessor" class="test.MyInstantiationAwareBeanPostProcessor"/>
<bean id="beanFactoryPostProcessor" class="test.MyBeanFactoryPostProcessor"/>
<bean id="car" class="test.Car" init-method="myInit"
destroy-method="myDestory" scope="singleton" p:carName="BMW"/>

下面来见证下整个流程

1
2
3
4
5
6
7
8
public static void main(String[] args) {
System.out.println("开始启动容器");
ApplicationContext factory = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
System.out.println("容器初始化成功");
Car car = factory.getBean("car",Car.class);
System.out.println("开始关闭容器");
((ClassPathXmlApplicationContext)factory).registerShutdownHook();
}

最终的console输出为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BeanFactoryPostProcessor构造函数
BeanFactoryPostProcessor.postProcessBeanFactory()
我现在可以修改beanDefination但是我这里就不修改了
InstantiationAwareBeanPostProcessorAdapter构造函数
BeanPostProcessor构造函数
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
bean的构造函数
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
InstantiationAwareBeanPostProcessor.postProcessPropertyValues()
属性注入
BeanNameAware.setBeanName()
BeanFactoryAware.setBeanFactory()
BeanPostProcessor.postProcessBeforeInitialization()
InitializingBean.afterPropertiesSet()
<bean>的init-method属性指定的初始化方法
BeanPostProcessor.postProcessAfterInitialization()
容器初始化成功
开始关闭容器
DiposibleBean.destory()
<bean>的destroy-method属性指定的初始化方法

1)容器启动阶段

容器需要依赖某些工具类(BeanDefinitionReader)对加载的Configuration MetaData进行解析和分析,并将分析后的信息编组为相应的BeanDefinition,最后把这些保存了bean定义必 要信息的BeanDefinition,注册到相应的BeanDefinitionRegistry,这样容器启动工作就完成了。
BeanDefinitionReader

1)用来和配置文件打交道

2)负责从对应的配置文件中读取内容并将bean映射到BeanDefinition

3)然后将BeanDefinition注册到BeanDefinitionRegistry中

eg:PropertiesBeanDefinitionReader、XmlBeanDefinitionReader
1
2
3
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanDefinitionRegistry);
reader.loadBeanDefinitions("classpath:xxx");
return (BeanFactory) beanDefinitionRegistry;


唯一能插手容器启动阶段的大佬—–BeanFactoryPostProcessor
在容器实例化对象之前,对注册到容器的BeanDefinition信息进行修改

下面是几个比较典型的BeanFactoryPostProcessor

* PropertyPlaceholderConfigurer

在实例化bean前,将bean配置中的占位符替换为实际值

1
2
3
4
5
6
7
8
9
10
11
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>

1
2
3
4
5
# jdbc.properties
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

在运行时,占位符内的内容会被替换为属性文件中的匹配的value,这个工作就是由PlaceholderConfiguer完成的,不难看出这个类应该实现了BeanFactoryPostProcessor

  • CustomEditorConfigurer

即容器从XML格式的文件中读取的都是字符串形式,最终应用程序却是由各种类型的对象所构成。 要想完成这种由字符串到具体对象的转换(不管这个转换工作最终由谁来做),都需要这种转换规则 相关的信息,而CustomEditorConfigurer就是帮助我们传达类似信息的。

如下我们需要将String类型的2018/07/27转为date

1
2
3
4
5
<bean id="dateFoo" class="...DateFoo">
<property name="date">
<value>2018/07/27</value>
</property>
</bean>

用户可以自定义CustomEditorConfigurer,来指定string到特定类型转换到逻辑

  • BeanFactoryPostProcessor的实际应用例子

之前项目中使用JUNIT进行单元测试,但是每次单元测试都要加载所有的bean,项目规模如果较大,那么单元测试启动需要很长时间,为了解决这个问题开发了一个简单的BeanFactoryPostProcessor,原理很简单就是将所有的bean都设置为懒加载模式,单元测试中用到哪个实例化哪个

1
2
3
4
5
6
7
8
9
10
public class LazyBeanFactoryProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
DefaultListableBeanFactory fac = (DefaultListableBeanFactory) beanFactory;
Map<String, AbstractBeanDefinition> map = (Map<String, AbstractBeanDefinition>) ReflectionTestUtils.getField(fac, "beanDefinitionMap");
for (Map.Entry<String, AbstractBeanDefinition> entry : map.entrySet()) {
entry.getValue().setLazyInit(true);
}
}
}

2)Bean实例化阶段
1.InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(Class beanClass,String beanName)
在执行Bean的构造器之前,如果有InstantiationAwareBeanPostProcessor那么会先执行它的postProcessBeforeInstantiation()如果此时返回的bean不为null,那么不会再继续执行后续的Bean流程,只执行postProcessAfterInitialization(),造成了”短路”
有一点需要注意的在提到InstantiationAwareBeanPostProcessor的方法的时候最好指定入参,因为它有好几个相同名字入参不同的方法,容易发生混淆

2.对bean进行实例化
容器采用策略模式来决定采用何种方式初始化bean实例(反射 or CGLIB默认)
如果配置的bean有look-up or replace此时需要使用CGLIB创建代理类

3.InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(Class beanClass,String beanName)

4.InstantiationAwareBeanPostProcessor.postProcessPropertiesValues(PropertyValues pvs,PropertyDescriptor[] pds, Object bean, String beanName)

5.对bean进行依赖注入
实例化后的产物是BeanWrapper实例,它是对bean的包裹,接下来对这个包裹设置属性值
Ps借助Wrapper可以用统一的方式对对象属性进行注入,BeanWrapper会使用PropertyEditor进行属性的类型转换

6.检查是否实现Aware接口(BeanFactory独有)
BeanNameAware:将BeanName设置到当前实例中
BeanClassLoaderAware:将加载当前bean的ClassLoader设置到当前实例中
BeanFactoryAware:将BeanFactory设置到当前实例中

7.BeanPostProcessor.postProcessBeforeInitialization
很多ApplicationContext独有的Aware接口也是通过BeanPostPorcessor实现属性注入的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Object postProcessBeforeInitialization(Object bean, String beanName) throws  BeansException {
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher
(this.applicationContext); }
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); }
return bean;
}

8.InititalizingBean.afterPropertiesSet()

9.init-method

10.BeanPostProcessor.postProcessAfterInitialization(Object bean, String beanName)

11.容器初始化成功,正常执行业务逻辑

12.DisposableBean.destroy()

13.destroy-method

循环依赖

Spring对于单例的bean之间的循环依赖会进行处理(Set注入),换言之对于单例构造器注入以及prototype类型的bean无法处理循环依赖。假设A依赖B,B又依赖于A,在实例化A的时候会将半成品的A放到缓存中,再去实例化所依赖的B,而在实例化B过程中又需要去实例化A,此时直接将半成品的A填充到B中,就完成了循环依赖Bean的实例化。

关于BeanPostProcessor

  • BeanPostProcessor处理容器内所有符合条件的BeanDefinition
    Ps这里的符合条件与否在覆写的方法中自行判断if(beanName==xxx)
  • A bean post-processor typically checks for callback interfaces or may wrap a bean with a proxy. Some Spring AOP infrastructure classes are implemented as bean post-processors in order to provide proxy-wrapping logic.
    AOP就是通过BeanPostProcessor实现偷梁换柱的
  • Classes that implement the BeanPostProcessor interface are special and are treated differently by the container. All BeanPostProcessors and beans that they reference directly are instantiated on startup, as part of the special startup phase of the ApplicationContext
    实现该接口的bean是很特殊的,它以及它所依赖的bean需要优先启动,可以将其视为容器启动的一个阶段。而且实现了该接口的bean(或者被依赖的beans)无法对其进行动态代理,因为动态代理本身就是通过BeanPostProcessor实现的,你可以理解为”大力士举不起自己”

举个栗子:
在refresh方法中调用的prepareBeanFactory中会执行beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));而这个beanPostProcessor方法详情如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}

该方法执行了applicationContext特有的一些aware的注入

关于InstantiationAwareBeanPostProcessor

这个类是一个特殊的BeanPostProcessor,通过前面的分析相信已经明确具体的回调位置了。举几个关于这个类的例子
CommonAnnotationBeanPostProcessor,AutowiredAnnotationBeanPostProcessor这两个类通过
postProcessPropertyValues方法实现了annotation为@reosource以及@autowire的属性的注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// CommonAnnotationBeanPostProcessor.class
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
// 找出bean中被@Resource注解修饰的属性(Field)和方法(Method)
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
try {
// 注入到bean中
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
}
return pvs;
}