Spring Bean生命週期,好像人的一生。。

大家好,我是老三,上節我們手擼了一個簡單的IOC容器五分鐘,手擼一個Spring容器!,這節我們來看一看Spring中Bean的生命週期,我發現,和人的一生真的很像。

簡單說說IoC和Bean

IoC,控制反轉,想必大家都知道,所謂的控制反轉,就是把new對象的權利交給容器,所有的對象都被容器控制,這就叫所謂的控制反轉。

控制反轉

Bean,也不是什麼新鮮玩意兒,它們就是一幫身不由己的Java對象,生命週期受到容器控制。

Bean生命週期和人生

Bean生命週期四大階段

我們知道,bean的作用域有好幾種,這篇文章只討論完全被IoC容器控制的單例Bean。

對於普通的Java對象來說,它們的生命週期就是:

  • 實例化
  • 對象不再被使用時通過垃圾回收機制進行回收

這就像是生活在大自然裏的動物,悄然出生,悄然死亡。

大象-圖片來源網絡

而對於Spring Bean的生命週期來說,可以分爲四個階段,其中初始化完成之後,就代表這個Bean可以使用了:

  • 實例化 Instantiation
  • 屬性賦值 Populate
  • 初始化 Initialization
  • 銷燬 Destruction

人和動物不一樣,存在非常複雜的社會。

高樓大廈中的行人

我們來看看社會里的人,一生要經歷哪些階段,是不是和Bean的生命週期很像呢?

  • 出生:作爲一個自然人降臨在這個世界
  • 登記:登記身份證號,姓名,正式成爲人類社會的一份子
  • 成長:接受教育,成爲對社會有用的人
  • 工作:爲社會創造價值
  • 死亡:人死如燈滅,不過人這盞燈滅了,還要把燈臺埋起來

image-20220303101042089

Bean實例化的時機也分爲兩種,BeanFactory管理的Bean是在使用到Bean的時候纔會實例化Bean,ApplicantContext管理的Bean在容器初始化的時候就回完成Bean實例化。

BeanFactory就是相對不那麼健全的原始一些的社會,ApplicantContext是發達健全的現代社會。

BeanFactory和Applicantcontext

Bean詳細生命週期

我們講到了Bean容器四個階段,會有一些容器級的方法,進行前置和後置的處理,比如InstantiationAwareBeanPostProcessor、BeanPostProcessor接口方法。這些方法獨立於Bean之外,並且會註冊到Spring容器中,在Spring容器創建Bean的時候,進行一些處理。

後處理器

這就好像,孩子出生之前,需要做一些準備,比如備孕、養胎、備產什麼的,出生之後,需要做一些護理。孩子上學前後,也需要做一些學籍的管理。

那麼有了各種各樣的擴展之後,我們再接着看看Bean的詳細的生命週期。首先,我們面臨一個問題——Bean的生命週期從什麼時候開始的呢?

上面寫了,Bean實例化前後,可以進行一些處理,但是如果從Bean實例化前算開始,那麼就要追溯到容器的初始化、beanDefiinition的加載開始。

所以這篇文章裏,我們取生命週期直接從Bean實例化開始,但是大家也要知道,Bean實例化前後,可以使用後處理器進行處理,例如BeanFactoryPostProcessor、InstantiationAwareBeanPostProcessor。

大家也不要困擾,就像計算人生的起點,是從母親懷孕算起,還是從孩子出生算起?我們這裏取了出生開始而已。

Bean生命週期

  • 實例化:第 1 步,實例化一個 Bean 對象
  • 屬性賦值:第 2 步,爲 Bean 設置相關屬性和依賴
  • 初始化:初始化的階段的步驟比較多,5、6步是真正的初始化,第 3、4 步爲在初始化前執行,第 7 步在初始化後執行,初始化完成之後,Bean就可以被使用了
  • 銷燬:第 8~10步,第8步其實也可以算到銷燬階段,但不是真正意義上的銷燬,而是先在使用前註冊了銷燬的相關調用接口,爲了後面第9、10步真正銷燬 Bean 時再執行相應的方法

我們發現Bean生命週期的詳細過程,是不是也像人生的歷程,出生、登記,不過是很短的事情。慢慢長大成人,要經歷人生的四分之一,而成長,來源於教育,不管是學校的還是社會的,接受教育前,要登記學籍,上學的時候,自己還要努力……,到最後,要發一紙薄薄的文憑,標誌着我們成爲可以捶打的“社會人”。

然後,爲社會奉獻四十年。最後老去,離世。不過Bean的世界,沒有退休——當然,也許,人的世界也沒有退休。

人的曲線

我們發現中間的一些擴展過程也可以分四類:

Bean週期四類過程

  • 一:獲取社會資源/Aware接口:Aware接口的作用是讓Bean能拿到容器的一些資源,例如BeanNameAware可以拿到BeanName。就好像上學之前,要取一個學名——不知道多少人上學之前不知道自己大名叫什麼,是吧?二毛。

  • 二:必備各種手續和證/後處理器:在Bean的生命週期裏,會有一些後處理器,它們的作用就是進行一些前置和後置的處理,就像上學之前,需要登記學籍,上學之後,會拿到畢業證。

  • 三:個人選擇/生命週期接口:人可能無法選擇如何出生,但也許可以選擇如何活着和如何死去,InitializingBean和DisposableBean 接口就是用來定義初始化方法和銷燬方法的。

  • 四:主觀能動/配置生命週期方法:環境影響人,人也在影響環境,成長的時候認真努力,衰亡的時候也可以豁達樂觀。可以通過配置文件,自定義初始化和銷燬方法。

PersonBean的一生

話不多說,接下來我們拿一個例子,來看看PersonBean的一生,我們先來看一下它的流程!

PersonBean的一生

用文字描述一下這個過程:

  1. Bean容器在配置文件中找到Person Bean的定義,這個可以說是媽媽懷上了。
  2. Bean容器使用Java 反射API創建Bean的實例,孩子出生了。
  3. Person聲明瞭屬性no、name,它們會被設置,相當於註冊身份證號和姓名。如果屬性本身是Bean,則將對其進行解析和設置。
  4. Person類實現了BeanNameAware接口,通過傳遞Bean的名稱來調用setBeanName()方法,相當於起個學名。
  5. Person類實現了BeanFactoryAware接口,通過傳遞BeanFactory對象的實例來調用setBeanFactory()方法,就像是選了一個學校。
  6. PersonBean實現了BeanPostProcessor接口,在初始化之前調用用postProcessBeforeInitialization()方法,相當於入學報名。
  7. PersonBean類實現了InitializingBean接口,在設置了配置文件中定義的所有Bean屬性後,調用afterPropertiesSet()方法,就像是入學登記。
  8. 配置文件中的Bean定義包含init-method屬性,該屬性的值將解析爲Person類中的方法名稱,初始化的時候會調用這個方法,成長不是走個流程,還需要自己不斷努力。
  9. Bean Factory對象如果附加了Bean 後置處理器,就會調用postProcessAfterInitialization()方法,畢業了,總得拿個證。
  10. Person類實現了DisposableBean接口,則當Application不再需要Bean引用時,將調用destroy()方法,簡單說,就是人掛了。
  11. 配置文件中的Person Bean定義包含destroy-method屬性,所以會調用Person類中的相應方法定義,相當於選好地兒,埋了。

我們來看看代碼!

PersonBean類

創建一個PersonBean,讓它實現幾個特殊的接口,我們來觀察一下它的生命週期的流轉。

public class PersonBean implements InitializingBean, BeanFactoryAware, BeanNameAware, DisposableBean {

    /**
     * 身份證號
     */
    private Integer no;

    /**
     * 姓名
     */
    private String name;

    public PersonBean() {
        System.out.println("1.調用構造方法:我出生了!");
    }

    public Integer getNo() {
        return no;
    }

    public void setNo(Integer no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("2.設置屬性:我的名字叫"+name);
    }

    @Override
    public void setBeanName(String s) {
        System.out.println("3.調用BeanNameAware#setBeanName方法:我要上學了,起了個學名");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("4.調用BeanFactoryAware#setBeanFactory方法:選好學校了");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("6.InitializingBean#afterPropertiesSet方法:入學登記");
    }

    public void init() {
        System.out.println("7.自定義init方法:努力上學ing");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("9.DisposableBean#destroy方法:平淡的一生落幕了");
    }

    public void destroyMethod() {
        System.out.println("10.自定義destroy方法:睡了,別想叫醒我");
    }

    public void work(){
        System.out.println("Bean使用中:工作,只有對社會沒有用的人才放假。。");
    }

}

  • 實現了InitializingBean, BeanFactoryAware, BeanNameAware, DisposableBean四個接口
  • 定義了no、name兩個屬性和對應的getter、setter方法
  • 定義了一個實例方法work

MyBeanPostProcessor

自定義了一個後處理器MyBeanPostProcessor:

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("5.BeanPostProcessor.postProcessBeforeInitialization方法:到學校報名啦");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("8.BeanPostProcessor#postProcessAfterInitialization方法:終於畢業,拿到畢業證啦!");
        return bean;
    }
}

配置文件

定義一個配置文件spring-config.xml:

  • 使用setter注入
  • 定義init-method和destroy-method
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="myBeanPostProcessor" class="cn.fighter3.spring.life.MyBeanPostProcessor" />
    <bean name="personBean" class="cn.fighter3.spring.life.PersonBean"
          init-method="init" destroy-method="destroyMethod">
        <property name="idNo" value= "80669865"/>
        <property name="name" value="張鐵鋼" />
    </bean>

</beans>

測試

最後測試一下,觀察PersonBean的生命週期的流轉:

public class Main {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        PersonBean personBean = (PersonBean) context.getBean("personBean");
        personBean.work();
        ((ClassPathXmlApplicationContext) context).destroy();
    }
}

運行結果:

1.調用構造方法:我出生了!
2.設置屬性:我的名字叫張鐵鋼
3.調用BeanNameAware#setBeanName方法:我要上學了,起了個學名
4.調用BeanFactoryAware#setBeanFactory方法:選好學校了
5.BeanPostProcessor#postProcessBeforeInitialization方法:到學校報名啦
6.InitializingBean#afterPropertiesSet方法:入學登記
7.自定義init方法:努力上學ing
8.BeanPostProcessor#postProcessAfterInitialization方法:終於畢業,拿到畢業證啦!
Bean使用中:工作,只有對社會沒有用的人才放假。。
9.DisposableBean#destroy方法:平淡的一生落幕了
10.自定義destroy方法:睡了,別想叫醒我

看看,是不是和我們圖中的流程一致。

這篇文章就不帶大家跟進更多的源碼了,如果大家對源碼級別的Bean的生命週期感興趣,可以看看AbstractApplicationContext類裏的refresh方法,這個方法是AplicationContext容器初始化的關鍵點。在這個方法裏,調用了finishBeanFactoryInitialization方法,這個方法裏調用了getBean方法,getBean方法裏調用了AbstractBeanFactorygetBean方法。

Bean生命週期源碼追蹤

最終經過一陣七拐八繞,到達了我們的目標——Bean創建的方法:doGetBean方法,在這個方法裏可以看到Bean的實例化,賦值、初始化的過程,至於最終的銷燬,可以看看ConfigurableApplicationContext#close()

結語

到這,這篇Bean的生命週期文章就走向destory了,自定義destory方法——回顧一下這篇文章的“一生”。

  • Bean的生命週期大致可以分爲四個階段:實例化、屬性賦值、初始化、銷燬,對應人生的出生、登記、成長、離世。
  • Bean生命週期中可以有很多擴展,就像人生的走向,會受很多影響,社會的環境、自身的選擇、自己的努力。


參考:

[1]. 《Spring揭祕》

[2]. Spring官網

[3].《精通Spring4.X企業應用開發實戰》

[4] .Spring Bean 生命週期 (實例結合源碼徹底講透)

[5].一文讀懂 Spring Bean 的生命週期

[6].如何記憶 Spring Bean 的生命週期

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章