大家好,我是老三,上節我們手擼了一個簡單的IOC容器五分鐘,手擼一個Spring容器!,這節我們來看一看Spring中Bean的生命週期,我發現,和人的一生真的很像。
簡單說說IoC和Bean
IoC,控制反轉,想必大家都知道,所謂的控制反轉,就是把new物件的權利交給容器,所有的物件都被容器控制,這就叫所謂的控制反轉。
Bean,也不是什麼新鮮玩意兒,它們就是一幫身不由己的Java物件,生命週期受到容器控制。
Bean生命週期和人生
Bean生命週期四大階段
我們知道,bean的作用域有好幾種,這篇文章只討論完全被IoC容器控制的單例Bean。
對於普通的Java物件來說,它們的生命週期就是:
- 例項化
- 物件不再被使用時通過垃圾回收機制進行回收
這就像是生活在大自然裡的動物,悄然出生,悄然死亡。
而對於Spring Bean的生命週期來說,可以分為四個階段,其中初始化完成之後,就代表這個Bean可以使用了:
- 例項化 Instantiation
- 屬性賦值 Populate
- 初始化 Initialization
- 銷燬 Destruction
人和動物不一樣,存在非常複雜的社會。
我們來看看社會裡的人,一生要經歷哪些階段,是不是和Bean的生命週期很像呢?
- 出生:作為一個自然人降臨在這個世界
- 登記:登記身份證號,姓名,正式成為人類社會的一份子
- 成長:接受教育,成為對社會有用的人
- 工作:為社會創造價值
- 死亡:人死如燈滅,不過人這盞燈滅了,還要把燈臺埋起來
Bean例項化的時機也分為兩種,BeanFactory管理的Bean是在使用到Bean的時候才會例項化Bean,ApplicantContext管理的Bean在容器初始化的時候就回完成Bean例項化。
BeanFactory就是相對不那麼健全的原始一些的社會,ApplicantContext是發達健全的現代社會。
Bean詳細生命週期
我們講到了Bean容器四個階段,會有一些容器級的方法,進行前置和後置的處理,比如InstantiationAwareBeanPostProcessor、BeanPostProcessor介面方法。這些方法獨立於Bean之外,並且會註冊到Spring容器中,在Spring容器建立Bean的時候,進行一些處理。
這就好像,孩子出生之前,需要做一些準備,比如備孕、養胎、備產什麼的,出生之後,需要做一些護理。孩子上學前後,也需要做一些學籍的管理。
那麼有了各種各樣的擴充套件之後,我們再接著看看Bean的詳細的生命週期。首先,我們面臨一個問題——Bean的生命週期從什麼時候開始的呢?
上面寫了,Bean例項化前後,可以進行一些處理,但是如果從Bean例項化前算開始,那麼就要追溯到容器的初始化、beanDefiinition的載入開始。
所以這篇文章裡,我們取生命週期直接從Bean例項化開始,但是大家也要知道,Bean例項化前後,可以使用後處理器進行處理,例如BeanFactoryPostProcessor、InstantiationAwareBeanPostProcessor。
大家也不要困擾,就像計算人生的起點,是從母親懷孕算起,還是從孩子出生算起?我們這裡取了出生開始而已。
- 例項化:第 1 步,例項化一個 Bean 物件
- 屬性賦值:第 2 步,為 Bean 設定相關屬性和依賴
- 初始化:初始化的階段的步驟比較多,5、6步是真正的初始化,第 3、4 步為在初始化前執行,第 7 步在初始化後執行,初始化完成之後,Bean就可以被使用了
- 銷燬:第 8~10步,第8步其實也可以算到銷燬階段,但不是真正意義上的銷燬,而是先在使用前註冊了銷燬的相關呼叫介面,為了後面第9、10步真正銷燬 Bean 時再執行相應的方法
我們發現Bean生命週期的詳細過程,是不是也像人生的歷程,出生、登記,不過是很短的事情。慢慢長大成人,要經歷人生的四分之一,而成長,來源於教育,不管是學校的還是社會的,接受教育前,要登記學籍,上學的時候,自己還要努力……,到最後,要發一紙薄薄的文憑,標誌著我們成為可以捶打的“社會人”。
然後,為社會奉獻四十年。最後老去,離世。不過Bean的世界,沒有退休——當然,也許,人的世界也沒有退休。
我們發現中間的一些擴充套件過程也可以分四類:
-
一:獲取社會資源/Aware介面:Aware介面的作用是讓Bean能拿到容器的一些資源,例如BeanNameAware可以拿到BeanName。就好像上學之前,要取一個學名——不知道多少人上學之前不知道自己大名叫什麼,是吧?二毛。
-
二:必備各種手續和證/後處理器:在Bean的生命週期裡,會有一些後處理器,它們的作用就是進行一些前置和後置的處理,就像上學之前,需要登記學籍,上學之後,會拿到畢業證。
-
三:個人選擇/生命週期介面:人可能無法選擇如何出生,但也許可以選擇如何活著和如何死去,InitializingBean和DisposableBean 介面就是用來定義初始化方法和銷燬方法的。
-
四:主觀能動/配置生命週期方法:環境影響人,人也在影響環境,成長的時候認真努力,衰亡的時候也可以豁達樂觀。可以通過配置檔案,自定義初始化和銷燬方法。
PersonBean的一生
話不多說,接下來我們拿一個例子,來看看PersonBean的一生,我們先來看一下它的流程!
用文字描述一下這個過程:
- Bean容器在配置檔案中找到Person Bean的定義,這個可以說是媽媽懷上了。
- Bean容器使用Java 反射API建立Bean的例項,孩子出生了。
- Person宣告瞭屬性no、name,它們會被設定,相當於註冊身份證號和姓名。如果屬性本身是Bean,則將對其進行解析和設定。
- Person類實現了
BeanNameAware
介面,通過傳遞Bean的名稱來呼叫setBeanName()
方法,相當於起個學名。 - Person類實現了
BeanFactoryAware
介面,通過傳遞BeanFactory物件的例項來呼叫setBeanFactory()
方法,就像是選了一個學校。 - PersonBean實現了BeanPostProcessor介面,在初始化之前呼叫用
postProcessBeforeInitialization()
方法,相當於入學報名。 - PersonBean類實現了
InitializingBean
介面,在設定了配置檔案中定義的所有Bean屬性後,呼叫afterPropertiesSet()
方法,就像是入學登記。 - 配置檔案中的Bean定義包含
init-method
屬性,該屬性的值將解析為Person類中的方法名稱,初始化的時候會呼叫這個方法,成長不是走個流程,還需要自己不斷努力。 - Bean Factory物件如果附加了Bean 後置處理器,就會呼叫
postProcessAfterInitialization()
方法,畢業了,總得拿個證。 - Person類實現了
DisposableBean
介面,則當Application不再需要Bean引用時,將呼叫destroy()
方法,簡單說,就是人掛了。 - 配置檔案中的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
方法裡呼叫了AbstractBeanFactory
的getBean
方法。
最終經過一陣七拐八繞,到達了我們的目標——Bean建立的方法:doGetBean
方法,在這個方法裡可以看到Bean的例項化,賦值、初始化的過程,至於最終的銷燬,可以看看ConfigurableApplicationContext#close()
。
結語
到這,這篇Bean的生命週期文章就走向destory了,自定義destory方法——回顧一下這篇文章的“一生”。
- Bean的生命週期大致可以分為四個階段:例項化、屬性賦值、初始化、銷燬,對應人生的出生、登記、成長、離世。
- Bean生命週期中可以有很多擴充套件,就像人生的走向,會受很多影響,社會的環境、自身的選擇、自己的努力。
參考:
[1]. 《Spring揭祕》
[2]. Spring官網
[3].《精通Spring4.X企業應用開發實戰》