代碼分析BeanFactory中Bean的生命周期

這一章,我們通過一個具體的實例來更好的理解 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,這兩個可以以插件的形式注冊到容器中,完成解耦。

下一章,我們來分析一下Spring中BeanPostProcessor接口和InstantiationAwareBeanPostProcessor接口的源碼,來更好的理解Bean的實例化過程。謝謝關注!
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容