IoC容器背后的秘密
主要分为两个阶段:容器启动阶段、Bean实例化阶段。
容器启动阶段:
容器需要依赖某些工具类(BeanDefinitionReader)对加载的Configuration MetaData( 通常也就是XML格式的配置信息)。进行解析和分析,并将分析后的信息编组为相应的BeanDefinition,最后把这些保存了bean定义必要信息的BeanDefinition,注册到相应的BeanDefinitionRegistry,这样容器启动工作就完成了。
Bean实例化阶段:
该阶段,容器会首先检查所请求的对象之前是否已经初始化。如果没有,则会根据注册的BeanDefinition所提供的信息实例化被请求对象,并为其注入依赖。
Spring容器内部工作机制:
1.ResourceLoader从存储介质中加载Spring配置信息,并使用Resource表示这个配置文件的资源;
2.BeanDefinitionReader读取Resource所指向的配置文件资源,将每一个
3.容器扫描BeanDefinitionRegistry中的BeanDefinition,识别实现BeanFactoryPostProcessor接口(Bean工厂后处理器)的Bean,调用这些Bean对BeanDefinition进行加工处理;
4.从BeanDefinitionRegistry中取出加工后的BeanDefinition,并调用InstantiationStrategy进行Bean实例化的工作;
5.容器使用BeanWrapper对Bean进行封装,完成Bean属性的设置工作;
6.利用容器中注册的Bean后置处理器(实现BeanPostProcessor接口的Bean)对已经完成属性加工的Bean进行后续加工,装配出一个准备就绪的Bean。
插手“容器的启动”
Spring提供了BeanFactoryPostProcessor的容器扩展机制,允许我们在容器实例化相应对象之前,对注册到容器的BeanDefinition所保存的信息做相应的修改。相当于在容器实现的第一阶段最后加入一道工序,对最终的BeanDefinition做一些额外的操作,比如修改其中bean定义的某些属性,为bean定义增加其他信息等。一般情况下我们不会直接写该接口的实现类,而是使用Spring提供的相关类:PropertyPlaceholderConfigurer 、PropertyOverrideConfigurer、CustomEditorConfigurer。
1. PropertyPlaceholderConfigurer
PropertyPlaceholderConfigurer允许我们在XML配置文件中使用占位符(PlaceHolder),并将这些占位符所代表的资源单独配置到简单的properties文件中来加载。
conf/jdbc.properties
conf/mail.properties
或者
...
2. PropertyOverrideConfigurer
通过PropertyOverrideConfigurer对容器中配置的任何你想处理的bean定义的property信息进行覆盖替换。详情见P69。
配置在properties文件中的信息通常都以明文表示,PropertyOverrideConfigurer的父类PropertyResourceConfigurer 提供了一个protected类型的方法convertPropertyValue,允许子类覆盖这个方法对相应的配置项进行转换,如对加密后的字符串解密之后再覆盖到相应的bean定义中。当然,既然PropertyPlaceholderConfigurer也同样继承了PropertyResourceConfigurer,我们也可以针对PropertyPlaceholderConfigurer应用类似的功能。
(还是偏向于使用PropertyPlaceholderConfigurer,对配置文件中如用户名及密码加密了,想要解密即可使用PropertyPlaceholderConfigurer。)
public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
private String[] encryptPropNames ={"jdbc.user","jdbc.password"};
@Override
protected String convertProperty(String propertyName, String propertyValue) {
if(isEncryptProp(propertyName)){
String decryptValue = DESUtils.getDecryptString(propertyValue);
return decryptValue;
}else{
return propertyValue;
}
}
/**
* 判断是否是加密的属性
*/
private boolean isEncryptProp(String propertyName){
for(String encryptPropName:encryptPropNames){
if(encryptPropName.equals(propertyName)){
return true;
}
}
return false;
}
}
在applicationContext.xml中需要进行如下配置:
3.CustomEditorConfigurer
上面两个都是通过对BeanDefinition中的数据进行变更以达到某中目的,CustomEditorConfigurer只是辅助性地将后期会用到的信息注册到容器,对BeanDefinition没有做任何变动。实际应用时,主要是将XML中String字符串转化为对象类型。具体参照P72。下面的是Spring3.x中的实例:
public class Car {
private int maxSpeed;
private double price;
private String brand;
//getter和setter
}
public class Boss {
private String name;
private Car car;
//getter和setter
}
public class CustomCarEditor extends PropertyEditorSupport{
@Override
public void setAsText(String text) {
if(text == null || text.indexOf(",") == -1){
throw new IllegalArgumentException("设置的字符串格式不正确");
}
String[] infos = text.split(",");
Car car = new Car();
car.setBrand(infos[0]);
car.setMaxSpeed(Integer.parseInt(infos[1]));
car.setPrice(Double.parseDouble(infos[2]));
setValue(car);
}
}
配置文件:
class="org.springframework.beans.factory.config.CustomEditorConfigurer"> class="property.Boss">
Spring 2.0之后,比较提倡使用propertyEditorRegistrars属性来指定自定义的PropertyEditor。(P72)
public class CarPropertyEditorRegistrar implements PropertyEditorRegistrar{
private PropertyEditor propertyEditor;
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Car.class, getPropertyEditor());
}
public PropertyEditor getPropertyEditor() {
return propertyEditor;
}
public void setPropertyEditor(PropertyEditor propertyEditor) {
this.propertyEditor = propertyEditor;
}
}
配置文件:
class="org.springframework.beans.factory.config.CustomEditorConfigurer"> class="property.CarPropertyEditorRegistrar"> class="property.CustomCarEditor"> class="property.Boss">
Bean生命周期
1.bean的实例化与BeanWrapper
初始化bean实例的两种方式:通过反射或者CGLIB动态字节码生成来初始化相应的bean实例或者动态生成其子类。
- 实例化策略的抽象接口:org.springframework.beans.factory.support.InstantiationStrategy
- SimpleInstantiationStrategy通过反射来实例化对象实例,但不支持方法注入方式的对象实例化。
- CglibSubclassingInstantiationStrategy(默认)继承SimpleInstantiationStrategy的以反射方式实例化对象的功能,并且通过CGLIB的动态字节码生成功能,可动态生成某个类的子类,进而满足了方法注入所需的对象实例化需求。
注意:不是直接返回构造完成的对象实例,而是以BeanWrapper对构造完成的对象实例进行包裹,返回相应的BeanWrapper实例。
BeanWrapper可以以统一的方式对对象属性进行访问,用以修改对象属性。
2.各类Aware接口
Spring容器会检查当前对象实例是否实现了一系列的以Aware命名结尾的接口定义,比如:如果Bean实现了BeanNameAware接口,则将该Bean对应的名称传给setBeanName方法;如果Bean实现了BeanFactoryAware接口,则将BeanFactory对象传给setBeanFactory方法。
3. BeanPostProcessor
BeanPostProcessor前置处理将执行的方法:public Object postProcessAfterInitialization(Object bean, String beanName)
BeanPostProcessor后置处理将执行的方法:public Object postProcessBeforeInitialization(Object bean, String beanName)
两个方法都传入了对象实例的引用,可以对传入的对象实例进行任意操作。
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessInitialization:"+bean+","+beanName);
return bean;
}
//注意:可以在这两个方法中修改返回的Bean,甚至可以返回一个新的bean
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessInitialization:"+bean+","+beanName);
return bean;
}
}
此外,要注意在XML文件中配置该Bean后置处理器。
补充:有一种特殊类型的BeanPostProcessor:InstantiationAwareBeanPostProcessor 。实例化bean对象步骤之前,容器会首先检查容器中是否注册有该接口。如果有,首先使用相应的InstantiationAwareBeanPostProcessor来构造对象实例。
4. InitializingBean和init-method
检测当前对象是否实现了InitializingBean接口,如果是,则会调用其afterPropertiesSet()方法进一步调整对象实例的状态。该接口在Spring容器内部广泛使用,实际业务中使用有限。
Spring还提供了另一种方式来指定自定义的对象初始化操作,那就是在XML配置的时候,使用
5. DisposableBean与destroy-method
对于scope="prototype"的Bean,调用者负责Bean后续生命的管理。
容器将检查singleton类型的bean实例,看其是否实现了DisposableBean接口。或者其对应的bean定义是否通过
DisposableBean和destroy-method为对象提供了执行自定义销毁逻辑的机会 。
Bean生命周期个人总结:(可面试口述)
1.按照Bean的构造函数或工厂方法实例化Bean对象。(InstantiationAwareBeanPostProcessor较为特殊,可忽略。)
2.通过BeanWrapper设置对象属性。
3.检查是否实现了Aware相关的接口。(如果Bean实现了BeanNameAware接口,则将该Bean对应的名称传给setBeanName方法;如果Bean实现了BeanFactoryAware接口,则将BeanFactory对象传给setBeanFactory方法。 等等...)
4.如果Bean实现了BeanPostProcessor接口,则调用其前置处理方法(postProcessBeforeInitialization)。
5.若Bean实现了InitializingBean接口,调用其afterPropertiesSet()方法。或者执行
6.如果Bean实现了BeanPostProcessor接口,则调用其后置处理方法(postProcessAfterInitialization)。
7.对于scope="prototype"的Bean,调用者负责Bean后续生命的管理。对于scope="singleton"的Bean,若实现了DisposableBean接口,则调用destory方法。或者执行
Bean生命周期完整版:
1.若容器注册了InstantiationAwareBeanPostProcessor,将调用postProcessBeforeInstantiation()方法
2.按照Bean的构造函数或工厂方法实例化Bean
3.若容器注册了InstantiationAwareBeanPostProcessor,将调用postProcessAfterInstantiation()方法
4.调用InstantiationAwareBeanPostProcessor的postProcessPropertyValues()方法
5.调用Bean的属性设置方法设置属性值
6.如果Bean实现了BeanNameAware接口,则将该Bean对应的名称传给setBeanName方法
7.如果Bean实现了BeanFactoryAware接口,则将BeanFactory对象传给setBeanFactory方法
8.如果Bean实现了BeanPostProcessor接口,则调用其postProcessBeforeInitialization方法
9.如果Bean实现了InitializingBean接口,则调用其afterPropertySet方法
10.若在bean中有init-method属性,则执行这个初始化方法
11.如果有和Bean关联的BeanPostProcessors对象,则这些对象的postProcessAfterInitialization方法被调用
12.对于scope="prototype"的Bean,调用者负责Bean后续生命的管理。对于scope="singleton"的Bean,Bean将放入IOC容器的缓存池中,将Bean引用返回给调用者,Spring负责后续生命的管理
13.对于scope="singleton"的Bean,若实现了DisposableBean接口,则调用destory方法
14.对于scope="singleton"的Bean,若在bean中有destory-method属性,则执行这个销毁方法