这一章,我们通过一个具体的实例来更好的理解 Bean 生命周期的各个步骤。
1.首先我们创建一个 User Bean,并让它直接实现 Bean 级生命周期接口方法和 Bean 自身的方法。
package com.smart.spring_04;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import java.util.Date;
/**
* 实现BeanFactoryAware,BeanNameAware,InitializingBean,DisposableBean 四个接口
*/
public class User implements BeanFactoryAware,BeanNameAware,InitializingBean,DisposableBean{
private String userName;
private String passWord;
private int grade;
private Date loginDate;
public String sex;
private BeanFactory beanFactory;
private String beanName;
public User(){
System.out.println("进入User()的无参构造函数,进行实例化");
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
System.out.println("调用 User.setUserName()方法");
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
System.out.println("调用 User.setPassWord()方法");
this.passWord = passWord;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
System.out.println("调用 User.setGrade()方法");
this.grade = grade;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
System.out.println("调用 User.setSex()方法");
this.sex = sex;
}
public Date getLoginDate() {
return loginDate;
}
public void setLoginDate(Date loginDate) {
System.out.println("调用 User.setLoginDate()方法");
this.loginDate = loginDate;
}
@Override
public String toString() {
return "用户名: "+userName+" 密码: "+passWord+" 性别: "+sex+" 等级: "+grade+" 最近一次登录日期: "+loginDate;
}
// BeanFactoryAware接口方法
public void setBeanFactory(BeanFactory beanFactory)throws BeansException{
System.out.println("进入 BeanFactoryAware.setBeanFactory() 方法,beanFactory: "+beanFactory);
this.beanFactory = beanFactory;
}
// BeanNameAware接口方法
public void setBeanName(String beanname){
System.out.println("进入 BeanNameAware.setBeanName() 方法,beanname: "+beanname);
this.beanName = beanname;
}
// InitializingBean接口方法
public void afterPropertiesSet() throws Exception {
System.out.println("进入 InitializingBean.afterPropertiesSet() 方法");
}
// DisposableBean接口方法,销毁bean方式一
public void destroy() throws Exception {
System.out.println("进入 DisposableBean.destory() 方法");
}
//自定义Bean的初始化与销毁方法
public void myInit() {
System.out.println("进入 myInit(),将用户等级设置为999");
this.grade = 999;
}
//销毁bean方式二
public void myDestory() {
System.out.println("进入 myDestroy() 方法");
}
}
User 实现Bean 级生命周期接口方法:BeanFactoryAware,BeanNameAware,InitializingBean,DisposableBean,并且在最后自定义了 Bean 的初始化与销毁方法。
2.设置完Bean 级生命周期接口方法和 Bean 自身的方法后,我们来准备两个“后处理器”的接口实现,也就是容器级生命周期接口方法。
下面是 InstantiationAwareBeanPostProcessor 接口的实现
package com.smart.spring_04;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import java.beans.PropertyDescriptor;
/**
* 自定义适配器(实例化后处理器),仅对User Bean进行处理
*/
public class MyInstantiationAwareBeanPostProcessorAdapter extends InstantiationAwareBeanPostProcessorAdapter {
//实例化Bean前调用此方法
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if("user".equals(beanName)){
System.out.println("Bean初始前后操作,进入 MyInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() 方法");
}
return null;
}
//实例化Bean后调用此方法
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if("user".equals(beanName)){
System.out.println("Bean初始化后操作,进入 MyInstantiationAwareBeanPostProcessor.postProcessAfterInitialization() 方法");
}
return true;
}
//在设置某个属性时调用
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
if("user".equals(beanName)){
System.out.println("进入 InstantiationAwareBeanPostProcessor.postProcessPropertyValues() 方法,在设置某个属性时调用此方法");
}
return pvs;
}
}
这个处理器的作用就跟注释中写明的一样,在实例化Bean的前后和设置Bean属性时起作用,即实例化Bean之前(调用postProcessBeforeInstantiation方法)和实例化Bean之后(调用postProcessAfterInstantiation方法),设置Bean属性时调用 postProcessPropertyValues 方法。
注:InstantiationAwareBeanPostProcessorAdapter接口与BeanPostProcessor接口类似,只时回调时机不同,前者是后者的子接口,前者管理Bean的实例化和属性设置,后者在调用显示的初始化(比如配置文件中的init-method)之前完成一些定制的初始化任务,这两个接口的使用方法很相似,但是BeanPostProcessor接口不能返回null 值,也不能设置延迟初始化('default-lazy-init'属性),因为如果这样做,Spring容器将不会注册它们,自定义逻辑也就无法得到应用。
关于InstantiationAwareBeanPostProcessorAdapter接口与BeanPostProcessor接口详解,可以查看下面这篇文章:
http://uule.iteye.com/blog/2094549
下面是 BeanPostProcessor 接口的实现类,作用:对配置文件中所提供的属性设置值进行判断,执行相关的“补缺补漏”(比如配置文件中没有设定性别,我可以在这里设定性别)操作:
package com.smart.spring_04;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* 仅对User Bean进行处理,对配置文件所提供的属性设置值进行判断,执行相应的“查漏补缺”操作
*/
public class MyBeanPostProcessor implements BeanPostProcessor{
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("user")){
User user = (User)bean;
if (user.getGrade()==100){
System.out.println
("进入 MyBeanPostProcessor.postProcessBeforeInitialization() 方法,将等级调整为888级。");
user.setGrade(888);
}
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("user")){
User user = (User)bean;
if (user.getSex() == null){
System.out.println
("进入 MyBeanPostProcessor.postProcessAfterInitialization() 方法,将性别调整为男性。");
user.setSex("男");
}
}
return bean;
}
}
在 MyBeanPostProcessor 类的 postProcessBeforeInitialization( ) 方法中,我们判断在容器中目前处理的 Bean 是否为 user(Bean名称由<bean>中的id设置),如果是 user,那么判断此时 user 的 grade 是否为100,如果是那么就将 grade 设置为888,顺便在 postProcessAfterInitialization()方法中将配置文件中未设置的 sex 设置成“男”。
3.将容器级生命周期接口方法注册到 BeanFactory 容器中。
首先是配置文件xml的设置:
<bean id="user" class="com.smart.spring_04.User"
init-method="myInit"
destroy-method="myDestory"
p:userName="猪头家的阿狸"
p:passWord="123456"
p:grade="100"
scope="singleton"/>
通过 init-method, destroy-method 指定自定义的初始化与销毁方法, scope="singleton" 指定Bean的作用域为单例模式,注意,我此时并未指定sex属性。
接下来,创建测试类,我们来装载配置文件,并看看 Bean 的生命周期过程。
package com.smart.spring_04;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.util.Date;
/**
*测试Bean的生命周期
*/
public class BeanLifeCycle {
private static void lifeCycleBeanFactory(){
//指定配置文件路径
Resource resource = new ClassPathResource("Spring-config");
//使用 BeanFactory 装载配置文件,并启动容器
BeanFactory bf = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new
XmlBeanDefinitionReader((DefaultListableBeanFactory)bf);
reader.loadBeanDefinitions(resource);
//注册 MyBeanPostProcessor 后处理器
((ConfigurableBeanFactory)bf).addBeanPostProcessor(new MyBeanPostProcessor());
//注册 MyInstantiationAwareBeanPostProcessorAdapter 后处理器
((ConfigurableBeanFactory)bf).addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessorAdapter());
//测试开始
//第一次从容器中获取user,将触发容器实例化user,引发Bean生命周期方法的调用
User user1 = (User)bf.getBean("user");
user1.setLoginDate(new Date());
System.out.println(user1.toString());
//第二次从容器中获取user,将直接从缓存池中获取
User user2 = (User)bf.getBean("user");
//查看 user1 与 user2 是否指向同一引用
System.out.println("user1 与 user2 是否指向同一引用: "+(user1 == user2));
//测试完毕,关闭容器
((ConfigurableBeanFactory)bf).destroySingletons();
}
public static void main(String[] args) {
lifeCycleBeanFactory();
}
}
注:
(1)后处理器的实际调用顺序和注册顺序无关,在多个后处理器的情况下,必须通过实现 org.springframework.core.Ordered 接口来确定调用顺序。
(2)Spring容器会根据检查后处理器是否实现了 InstantiationAwareBeanPostProcessor 接口来判断后处理器的类型。
(3)我们在配置文件中显示指定了Bean的作用域为 singleton ,那么第一次从容器中获取user,将触发容器实例化user,引发Bean生命周期方法的调用,实例化后将其放入缓存池中,将引用返回给调用者,容器将继续管理Bean的后续生命过程。第二次从容器中获取user时,将不会触发实例化,不会引发Bean生命周期方法的调用,而是将缓存池中Bean的引用直接返回给你。若Bean的作用域为 prototype,那么将触发容器实例化user,引发Bean生命周期方法的调用,此时会创建一个新的Bean。
运行结果如下所示:
Bean初始前操作,进入 MyInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() 方法
进入User()的无参构造函数,进行实例化
Bean初始化后操作,进入 MyInstantiationAwareBeanPostProcessor.postProcessAfterInitialization() 方法
进入 InstantiationAwareBeanPostProcessor.postProcessPropertyValues() 方法,在设置某个属性时调用此方法
调用 User.setGrade()方法
调用 User.setPassWord()方法
调用 User.setUserName()方法
进入 BeanNameAware.setBeanName() 方法,beanname: user
进入 BeanFactoryAware.setBeanFactory() 方法,beanFactory: org.springframework.beans.factory.support.DefaultListableBeanFactory@573f2bb1: defining beans [car,boss,bosslist,user]; root of factory hierarchy
进入 MyBeanPostProcessor.postProcessBeforeInitialization() 方法,将等级调整为888级。
调用 User.setGrade()方法
进入 InitializingBean.afterPropertiesSet() 方法
进入 myInit(),将用户等级设置为999
进入 MyBeanPostProcessor.postProcessAfterInitialization() 方法,将性别调整为男性。
调用 User.setSex()方法
调用 User.setLoginDate()方法
用户名: 猪头家的阿狸 密码: 123456 性别: 男 等级: 999 最近一次登录日期: Sun Dec 03 14:27:34 CST 2017
user1 与 user2 是否指向同一引用: true
进入 DisposableBean.destory() 方法
进入 myDestroy() 方法
若将Bean的作用域为 prototype,打印结果如下:
Bean初始前操作,进入 MyInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() 方法
进入User()的无参构造函数,进行实例化
Bean初始化后操作,进入 MyInstantiationAwareBeanPostProcessor.postProcessAfterInitialization() 方法
进入 InstantiationAwareBeanPostProcessor.postProcessPropertyValues() 方法,在设置某个属性时调用此方法
调用 User.setGrade()方法
调用 User.setPassWord()方法
调用 User.setUserName()方法
进入 BeanNameAware.setBeanName() 方法,beanname: user
进入 BeanFactoryAware.setBeanFactory() 方法,beanFactory: org.springframework.beans.factory.support.DefaultListableBeanFactory@3c756e4d: defining beans [car,boss,bosslist,user]; root of factory hierarchy
进入 MyBeanPostProcessor.postProcessBeforeInitialization() 方法,将等级调整为888级。
调用 User.setGrade()方法
进入 InitializingBean.afterPropertiesSet() 方法
进入 myInit(),将用户等级设置为999
进入 MyBeanPostProcessor.postProcessAfterInitialization() 方法,将性别调整为男性。
调用 User.setSex()方法
调用 User.setLoginDate()方法
用户名: 猪头家的阿狸 密码: 123456 性别: 男 等级: 999 最近一次登录日期: Sun Dec 03 14:30:00 CST 2017
进入User()的无参构造函数,进行实例化
Bean初始化后操作,进入 MyInstantiationAwareBeanPostProcessor.postProcessAfterInitialization() 方法
进入 InstantiationAwareBeanPostProcessor.postProcessPropertyValues() 方法,在设置某个属性时调用此方法
调用 User.setGrade()方法
调用 User.setPassWord()方法
调用 User.setUserName()方法
进入 BeanNameAware.setBeanName() 方法,beanname: user
进入 BeanFactoryAware.setBeanFactory() 方法,beanFactory: org.springframework.beans.factory.support.DefaultListableBeanFactory@3c756e4d: defining beans [car,boss,bosslist,user]; root of factory hierarchy
进入 MyBeanPostProcessor.postProcessBeforeInitialization() 方法,将等级调整为888级。
调用 User.setGrade()方法
进入 InitializingBean.afterPropertiesSet() 方法
进入 myInit(),将用户等级设置为999
进入 MyBeanPostProcessor.postProcessAfterInitialization() 方法,将性别调整为男性。
调用 User.setSex()方法
user1 与 user2 是否指向同一引用: false
结果证明了:对于singleton的Bean,Spring会处理后续生命周期的管理工作,而对于prototype的Bean,最后将由调用者自己负责,Spring将不处理销毁。
4.关于 Bean 生命周期接口耦合性的思考
同志们啊!你们有没有发现,在创建User Bean 时,居然实现了四个接口,这跟Spring一直以来推崇的“ 不对应用程序类做任何限制 ”的理念也差的太远了吧。其实我们在使用自己的Bean时,完全不需要通过实现 InitializingBean 和 DisposableBean 接口,我们可以在配置文件中设定 init-method, destroy-method 指定自定义的初始化与销毁方法,这两中方式其实差不多,后者还达到了框架解耦目的。当然,除了这两种方式,Spring还提供了另一种 Bean 后置处理器 InitDestoryAnnotationBeanPOstProcessor,这个处理器提供了一些注解:@PostConstruct,@PreDestory ,用来在Bean初始化后及销毁前执行相应的逻辑(在ApplicationContext中,已经默认装配该处理器)。
对于另外两个接口BeanFactoryAware,BeanNameAware,前者让 Bean 感知容器(BeanFactory 实例),后者让Bean 获得配置文件中对应的配置名称,一般情况下,我们不需要关心这两个接口,我们可以使用属性注入的方式引用这些Bean。
关于 BeanPostProcessor 和 InstantiationAwareBeanPostProcessor,这两个可以以插件的形式注册到容器中,完成解耦。
网友评论