getBean()
方法來請求某個實體物件的時候,它才會觸發相應 bean 的實體化行程,當然也可以選擇直接使用 ApplicationContext 容器,因為該容器啟動的時候會立刻呼叫註冊到該容器所有 bean 定義的實體化方法。當然對於 BeanFactory 容器而言並不是所有的 getBean()
方法都會觸發實體化行程,比如 signleton 型別的 bean,該型別的 bean 只會在第一次呼叫 getBean()
的時候才會觸發,而後續的呼叫則會直接傳回容器快取中的實體物件。
getBean()
只是 bean 實體化行程的入口,真正的實現邏輯其實是在 AbstractAutowireCapableBeanFactory 的 doCreateBean()
實現,實體化過程如下圖:
原來我們採用 new 的方式建立一個物件,用完該物件在其脫離作用域後就會被回收,對於後續操作我們無權也沒法干涉,但是採用 Spring 容器後,我們完全擺脫了這種命運,Spring 容器將會對其所有管理的 Bean 物件全部給予一個統一的生命週期管理,同時在這個階段我們也可以對其進行干涉(比如對 bean 進行增強處理,對 bean 進行篡改),如上圖。
bean 實體化
在 doCreateBean()
中首先進行 bean 實體化工作,主要由 createBeanInstance()
實現,該方法傳回一個 BeanWrapper 物件。BeanWrapper 物件是 Spring 的一個低階 Bean 基礎結構的核心介面,為什麼說是低階呢?因為這個時候的 Bean 還不能夠被我們使用,連最基本的屬性都沒有設定。而且在我們實際開發過程中一般都不會直接使用該類,而是透過 BeanFactory 隱式使用。
BeanWrapper 介面有一個預設實現類 BeanWrapperImpl,其主要作用是對 Bean 進行“包裹”,然後對這個包裹的 bean 進行操作,比如後續註入 bean 屬性。
在實體化 bean 過程中,Spring 採用“策略樣式”來決定採用哪種方式來實體化 bean,一般有反射和 CGLIB 動態位元組碼兩種方式。
InstantiationStrategy 定義了 Bean 實體化策略的抽象介面,其子類 SimpleInstantiationStrategy 提供了基於反射來實體化物件的功能,但是不支援方法註入方式的物件實體化。CglibSubclassingInstantiationStrategy 繼承 SimpleInstantiationStrategy,他除了擁有父類以反射實體化物件的功能外,還提供了透過 CGLIB 的動態位元組碼的功能進而支援方法註入所需的物件實體化需求。預設情況下,Spring 採用 CglibSubclassingInstantiationStrategy。
關於 Bean 實體化的詳細過程,請參考以下幾篇文章:
- 【死磕 Spring】—– IOC 之載入 bean:建立 bean(一)
- 【死磕 Spring】—– IOC 之載入 bean:建立 bean(二)
- 【死磕 Spring】—– IOC 之載入 bean:建立 bean(三)
- 【死磕 Spring】—– IOC 之載入 bean:建立 bean(四)
- 【死磕 Spring】—– IOC 之載入 bean:建立 bean(五)
- 【死磕 Spring】—– IOC 之載入 bean:建立 bean(六)
- 【死磕 Spring】—– IOC 之載入 bean:總結
對於 BeanWrapper 和 具體的實體化策略,LZ 在後面會專門寫文章來進行詳細說明。
啟用 Aware
當 Spring 完成 bean 物件實體化並且設定完相關屬性和依賴後,則會開始 bean 的初始化行程( initializeBean()
),初始化第一個階段是檢查當前 bean 物件是否實現了一系列以 Aware 結尾的的介面。
Aware 介面為 Spring 容器的核心介面,是一個具有標識作用的超級介面,實現了該介面的 bean 是具有被 Spring 容器通知的能力,通知的方式是採用回呼的方式。
在初始化階段主要是感知 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware :
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
- BeanNameAware:對該 bean 物件定義的 beanName 設定到當前物件實體中
- BeanClassLoaderAware:將當前 bean 物件相應的 ClassLoader 註入到當前物件實體中
- BeanFactoryAware:BeanFactory 容器會將自身註入到當前物件實體中,這樣當前物件就會擁有一個 BeanFactory 容器的取用。
當然,Spring 不僅僅只是提供了上面三個 Aware 介面,而是一系列:
- LoadTimeWeaverAware:載入Spring Bean時織入第三方模組,如AspectJ
- BootstrapContextAware:資源配接器BootstrapContext,如JCA,CCI
- ResourceLoaderAware:底層訪問資源的載入器
- PortletConfigAware:PortletConfig
- PortletContextAware:PortletContext
- ServletConfigAware:ServletConfig
- ServletContextAware:ServletContext
- MessageSourceAware:國際化
- ApplicationEventPublisherAware:應用事件
- NotificationPublisherAware:JMX通知
更多關於 Aware 的請關註:【死磕 Spring】—– IOC 之 深入分析 Aware 介面
BeanPostProcessor
初始化第二個階段則是 BeanPostProcessor 增強處理,在該階段 BeanPostProcessor 會處理當前容器內所有符合條件的實體化後的 bean 物件。它主要是對 Spring 容器提供的 bean 實體物件進行有效的擴充套件,允許 Spring 在初始化 bean 階段對其進行定製化修改,如處理標記介面或者為其提供代理實現。
BeanPostProcessor 介面提供了兩個方法,在不同的時機執行,分別對應上圖的前置處理和後置處理。
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
更多關於 BeanPostProcessor 的請關註:【死磕 Spring】—– IOC 之 深入分析 BeanPostProcessor
InitializingBean 和 init-method
InitializingBean 是一個介面,它為 Spring Bean 的初始化提供了一種方式,它有一個 afterPropertiesSet()
方法,在 bean 的初始化行程中會判斷當前 bean 是否實現了 InitializingBean,如果實現了則呼叫 afterPropertiesSet()
進行初始化工作。然後再檢查是否也指定了 init-method(),如果指定了則透過反射機制呼叫指定的 init-method()。
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
對於 Spring 而言,雖然上面兩種方式都可以實現初始化定製化,但是更加推崇 init-method
方式,因為對於 InitializingBean 介面而言,他需要 bean 去實現介面,這樣就會汙染我們的應用程式,顯得 Spring 具有一定的侵入性。但是由於 init-method
是採用反射的方式,所以執行效率上相對於 InitializingBean 介面回呼的方式可能會低一些。
更多關於 inti 的請關註:【死磕 Spring】—– IOC 之 深入分析 InitializingBean 和 init-method
DisposableBean 和 destroy-method
與 InitializingBean 和 init-method 用於物件的自定義初始化工作相似,DisposableBean和 destroy-method 則用於物件的自定義銷毀工作。
當一個 bean 物件經歷了實體化、設定屬性、初始化階段,那麼該 bean 物件就可以供容器使用了(呼叫的過程)。當完成呼叫後,如果是 singleton 型別的 bean ,則會看當前 bean 是否應實現了 DisposableBean 介面或者配置了 destroy-method 屬性,如果是的話,則會為該實體註冊一個用於物件銷毀的回呼方法,便於在這些 singleton 型別的 bean 物件銷毀之前執行銷毀邏輯。
但是,並不是物件完成呼叫後就會立刻執行銷毀方法,因為這個時候 Spring 容器還處於執行階段,只有當 Spring 容器關閉的時候才會去呼叫。但是, Spring 容器不會這麼聰明會自動去呼叫這些銷毀方法,而是需要我們主動去告知 Spring 容器。
- 對於 BeanFactory 容器而言,我們需要主動呼叫
destroySingletons()
通知 BeanFactory 容器去執行相應的銷毀方法。 - 對於 ApplicationContext 容器而言呼叫
registerShutdownHook()
方法。
實踐驗證
下麵用一個實體來真實看看看上面執行的邏輯,畢竟理論是不能缺少實踐的:
public class lifeCycleBean implements BeanNameAware,BeanFactoryAware,BeanClassLoaderAware,BeanPostProcessor,
InitializingBean,DisposableBean {
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
System.out.println("屬性註入....");
this.test = test;
}
public lifeCycleBean(){
System.out.println("建構式呼叫...");
}
public void display(){
System.out.println("方法呼叫...");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware 被呼叫...");
}
@Override
public void setBeanName(String name) {
System.out.println("BeanNameAware 被呼叫...");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("BeanClassLoaderAware 被呼叫...");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor postProcessBeforeInitialization 被呼叫...");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor postProcessAfterInitialization 被呼叫...");
return bean;
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean destroy 被調動...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean afterPropertiesSet 被調動...");
}
public void initMethod(){
System.out.println("init-method 被呼叫...");
}
public void destroyMethdo(){
System.out.println("destroy-method 被呼叫...");
}
}
lifeCycleBean 繼承了 BeanNameAware
, BeanFactoryAware
, BeanClassLoaderAware
, BeanPostProcessor
,InitializingBean
, DisposableBean
六個介面,同時定義了一個 test 屬性用於驗證屬性註入和提供一個 display()
用於模擬呼叫。 配置如下:
id="lifeCycle" class="org.springframework.core.test.lifeCycleBean"
init-method="initMethod" destroy-method="destroyMethdo">
name="test" value="test"/>
配置 init-method 和 destroy-method。測試方法如下:
// BeanFactory 容器一定要呼叫該方法進行 BeanPostProcessor 註冊
factory.addBeanPostProcessor(new lifeCycleBean());
lifeCycleBean lifeCycleBean = (lifeCycleBean) factory.getBean("lifeCycle");
lifeCycleBean.display();
System.out.println("方法呼叫完成,容器開始關閉....");
// 關閉容器
factory.destroySingletons();
執行結果:
建構式呼叫...
建構式呼叫...
屬性註入....
BeanNameAware 被呼叫...
BeanClassLoaderAware 被呼叫...
BeanFactoryAware 被呼叫...
BeanPostProcessor postProcessBeforeInitialization 被呼叫...
InitializingBean afterPropertiesSet 被調動...
init-method 被呼叫...
BeanPostProcessor postProcessAfterInitialization 被呼叫...
方法呼叫...
方法呼叫完成,容器開始關閉....
DisposableBean destroy 被調動...
destroy-method 被呼叫...
有兩個建構式呼叫是因為要註入一個 BeanPostProcessor(你也可以另外提供一個 BeanPostProcessor 實體)。
根據執行的結果已經上面的分析,我們就可以對 Spring Bean 的宣告週期過程如下(方法級別):
- Spring 容器根據實體化策略對 Bean 進行實體化。
- 實體化完成後,如果該 bean 設定了一些屬性的話,則利用 set 方法設定一些屬性。
- 如果該 Bean 實現了 BeanNameAware 介面,則呼叫
setBeanName()
方法。 - 如果該 bean 實現了 BeanClassLoaderAware 介面,則呼叫
setBeanClassLoader()
方法。 - 如果該 bean 實現了 BeanFactoryAware介面,則呼叫
setBeanFactory()
方法。 - 如果該容器註冊了 BeanPostProcessor,則會呼叫
postProcessBeforeInitialization()
方法完成 bean 前置處理 - 如果該 bean 實現了 InitializingBean 介面,則呼叫 。
afterPropertiesSet()
方法。 - 如果該 bean 配置了 init-method 方法,則呼叫 init-method 指定的方法。
- 初始化完成後,如果該容器註冊了 BeanPostProcessor 則會呼叫
postProcessAfterInitialization()
方法完成 bean 的後置處理。 - 物件完成初始化,開始方法呼叫。
- 在容器進行關閉之前,如果該 bean 實現了 DisposableBean 介面,則呼叫
destroy()
方法。 - 在容器進行關閉之前,如果該 bean 配置了 destroy-mehod,則呼叫其指定的方法。
- 到這裡一個 bean 也就完成了它的一生。