《Spring揭秘》——IOC梳理2(容器启动,bean生命周期)
IoC容器背后的秘密
主要分为两个阶段:容器启动阶段、Bean实例化阶段。
容器启动阶段:
容器需要依赖某些工具类(BeanDefinitionReader)对加载的Configuration MetaData( 通常也就是XML格式的配置信息)。进行解析和分析,并将分析后的信息编组为相应的BeanDefinition,最后把这些保存了bean定义必要信息的BeanDefinition,注册到相应的BeanDefinitionRegistry,这样容器启动工作就完成了。
Bean实例化阶段:
该阶段,容器会首先检查所请求的对象之前是否已经初始化。如果没有,则会根据注册的BeanDefinition所提供的信息实例化被请求对象,并为其注入依赖。
Spring容器内部工作机制:
1.ResourceLoader从存储介质中加载Spring配置信息,并使用Resource表示这个配置文件的资源;
2.BeanDefinitionReader读取Resource所指向的配置文件资源,将每一个<bean>解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中;
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文件中来加载。
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>conf/jdbc.properties</value> <value>conf/mail.properties</value> </list> </property> </bean>
或者<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.user}"></property> ... </bean>
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中需要进行如下配置:
<bean class="com.echo.utils.EncryptPropertyPlaceholderConfigurer" p:location="classpath:db.properties" p:fileEncoding="utf-8"></bean>
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); } }
配置文件:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="property.Car" value="property.CustomCarEditor"></entry> </map> </property> </bean> <bean id="boss" class="property.Boss"> <property name="name" value="John"></property> <property name="car" value="BWM,200,500000"></property> </bean>
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; } }
配置文件:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="propertyEditorRegistrars"> <list> <ref bean="carPropertyEditorRegistrar" /> </list> </property> </bean> <bean id="carPropertyEditorRegistrar" class="property.CarPropertyEditorRegistrar"> <property name="propertyEditor" ref="customCarEditor" /> </bean> <bean id="customCarEditor" class="property.CustomCarEditor"></bean> <bean id="boss" class="property.Boss"> <property name="name" value="John"></property> <property name="car" value="BWM,200,500000"></property> </bean>
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("postProcess<After>Initialization:"+bean+","+beanName); return bean; } //注意:可以在这两个方法中修改返回的Bean,甚至可以返回一个新的bean @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcess<Before>Initialization:"+bean+","+beanName); return bean; } }
此外,要注意在XML文件中配置该Bean后置处理器。
补充:有一种特殊类型的BeanPostProcessor:InstantiationAwareBeanPostProcessor 。实例化bean对象步骤之前,容器会首先检查容器中是否注册有该接口。如果有,首先使用相应的InstantiationAwareBeanPostProcessor来构造对象实例。
4. InitializingBean和init-method
检测当前对象是否实现了InitializingBean接口,如果是,则会调用其afterPropertiesSet()方法进一步调整对象实例的状态。该接口在Spring容器内部广泛使用,实际业务中使用有限。
Spring还提供了另一种方式来指定自定义的对象初始化操作,那就是在XML配置的时候,使用<bean>的init-method属性。
5. DisposableBean与destroy-method
对于scope="prototype"的Bean,调用者负责Bean后续生命的管理。
容器将检查singleton类型的bean实例,看其是否实现了DisposableBean接口。或者其对应的bean定义是否通过<bean>的destroy-method属性指定了自定义的对象销毁方法。
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()方法。或者执行<bean>的init-method方法。
6.如果Bean实现了BeanPostProcessor接口,则调用其后置处理方法(postProcessAfterInitialization)。
7.对于scope="prototype"的Bean,调用者负责Bean后续生命的管理。对于scope="singleton"的Bean,若实现了DisposableBean接口,则调用destory方法。或者执行<bean>的destory-method方法。
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属性,则执行这个销毁方法