這一章,我們通過一個具體的實例來更好的理解 Bean 生命周期的各個步驟。
1.首先我們創(chuàng)建一個 User Bean,并讓它直接實現(xiàn) Bean 級生命周期接口方法和 Bean 自身的方法。
package com.smart.spring_04;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import java.util.Date;
/**
* 實現(xiàn)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()的無參構造函數(shù),進行實例化");
}
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 實現(xiàn)Bean 級生命周期接口方法:BeanFactoryAware,BeanNameAware,InitializingBean,DisposableBean,并且在最后自定義了 Bean 的初始化與銷毀方法。
2.設置完Bean 級生命周期接口方法和 Bean 自身的方法后,我們來準備兩個“后處理器”的接口實現(xiàn),也就是容器級生命周期接口方法。
下面是 InstantiationAwareBeanPostProcessor 接口的實現(xiàn)
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 接口的實現(xiàn)類,作用:對配置文件中所提供的屬性設置值進行判斷,執(zhí)行相關的“補缺補漏”(比如配置文件中沒有設定性別,我可以在這里設定性別)操作:
package com.smart.spring_04;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* 僅對User Bean進行處理,對配置文件所提供的屬性設置值進行判斷,執(zhí)行相應的“查漏補缺”操作
*/
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屬性。
接下來,創(chuàng)建測試類,我們來裝載配置文件,并看看 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,將觸發(fā)容器實例化user,引發(fā)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)后處理器的實際調用順序和注冊順序無關,在多個后處理器的情況下,必須通過實現(xiàn) org.springframework.core.Ordered 接口來確定調用順序。
(2)Spring容器會根據(jù)檢查后處理器是否實現(xiàn)了 InstantiationAwareBeanPostProcessor 接口來判斷后處理器的類型。
(3)我們在配置文件中顯示指定了Bean的作用域為 singleton ,那么第一次從容器中獲取user,將觸發(fā)容器實例化user,引發(fā)Bean生命周期方法的調用,實例化后將其放入緩存池中,將引用返回給調用者,容器將繼續(xù)管理Bean的后續(xù)生命過程。第二次從容器中獲取user時,將不會觸發(fā)實例化,不會引發(fā)Bean生命周期方法的調用,而是將緩存池中Bean的引用直接返回給你。若Bean的作用域為 prototype,那么將觸發(fā)容器實例化user,引發(fā)Bean生命周期方法的調用,此時會創(chuàng)建一個新的Bean。
運行結果如下所示:
Bean初始前操作,進入 MyInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() 方法
進入User()的無參構造函數(shù),進行實例化
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()的無參構造函數(shù),進行實例化
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()的無參構造函數(shù),進行實例化
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會處理后續(xù)生命周期的管理工作,而對于prototype的Bean,最后將由調用者自己負責,Spring將不處理銷毀。
4.關于 Bean 生命周期接口耦合性的思考
??同志們?。∧銈冇袥]有發(fā)現(xiàn),在創(chuàng)建User Bean 時,居然實現(xiàn)了四個接口,這跟Spring一直以來推崇的“ 不對應用程序類做任何限制 ”的理念也差的太遠了吧。其實我們在使用自己的Bean時,完全不需要通過實現(xiàn) InitializingBean 和 DisposableBean 接口,我們可以在配置文件中設定 init-method, destroy-method 指定自定義的初始化與銷毀方法,這兩中方式其實差不多,后者還達到了框架解耦目的。當然,除了這兩種方式,Spring還提供了另一種 Bean 后置處理器 InitDestoryAnnotationBeanPOstProcessor,這個處理器提供了一些注解:@PostConstruct,@PreDestory ,用來在Bean初始化后及銷毀前執(zhí)行相應的邏輯(在ApplicationContext中,已經(jīng)默認裝配該處理器)。
??對于另外兩個接口BeanFactoryAware,BeanNameAware,前者讓 Bean 感知容器(BeanFactory 實例),后者讓Bean 獲得配置文件中對應的配置名稱,一般情況下,我們不需要關心這兩個接口,我們可以使用屬性注入的方式引用這些Bean。
??關于 BeanPostProcessor 和 InstantiationAwareBeanPostProcessor,這兩個可以以插件的形式注冊到容器中,完成解耦。