歡迎光臨
每天分享高質量文章

【死磕 Spring】—– IOC 之 分析 bean 的生命週期

在分析 Spring Bean 實體化過程中提到 Spring 並不是一啟動容器就開啟 bean 的實體化行程,只有當客戶端透過顯示或者隱式的方式呼叫 BeanFactory 的 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 :

  1. private void invokeAwareMethods(final String beanName, final Object bean) {
  2.  if (bean instanceof Aware) {
  3.   if (bean instanceof BeanNameAware) {
  4.    ((BeanNameAware) bean).setBeanName(beanName);
  5.   }
  6.   if (bean instanceof BeanClassLoaderAware) {
  7.    ClassLoader bcl = getBeanClassLoader();
  8.    if (bcl != null) {
  9.     ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
  10.    }
  11.   }
  12.   if (bean instanceof BeanFactoryAware) {
  13.    ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
  14.   }
  15.  }
  16. }
  • 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 介面提供了兩個方法,在不同的時機執行,分別對應上圖的前置處理和後置處理。

  1. public interface BeanPostProcessor {
  2. @Nullable
  3. default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  4.  return bean;
  5. }
  6.  
  7. @Nullable
  8. default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  9.  return bean;
  10. }
  11. }

更多關於 BeanPostProcessor 的請關註:【死磕 Spring】—– IOC 之 深入分析 BeanPostProcessor

InitializingBean 和 init-method

InitializingBean 是一個介面,它為 Spring Bean 的初始化提供了一種方式,它有一個 afterPropertiesSet() 方法,在 bean 的初始化行程中會判斷當前 bean 是否實現了 InitializingBean,如果實現了則呼叫 afterPropertiesSet() 進行初始化工作。然後再檢查是否也指定了 init-method(),如果指定了則透過反射機制呼叫指定的 init-method()。

  1. protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
  2.   throws Throwable {
  3.  
  4.  boolean isInitializingBean = (bean instanceof InitializingBean);
  5.  if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
  6.   if (logger.isDebugEnabled()) {
  7.    logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
  8.   }
  9.   if (System.getSecurityManager() != null) {
  10.    try {
  11.     AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
  12.      ((InitializingBean) bean).afterPropertiesSet();
  13.      return null;
  14.     }, getAccessControlContext());
  15.    }
  16.    catch (PrivilegedActionException pae) {
  17.     throw pae.getException();
  18.    }
  19.   }
  20.   else {
  21.    ((InitializingBean) bean).afterPropertiesSet();
  22.   }
  23.  }
  24.  
  25.  if (mbd != null && bean.getClass() != NullBean.class) {
  26.   String initMethodName = mbd.getInitMethodName();
  27.   if (StringUtils.hasLength(initMethodName) &&
  28.     !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
  29.     !mbd.isExternallyManagedInitMethod(initMethodName)) {
  30.    invokeCustomInitMethod(beanName, bean, mbd);
  31.   }
  32.  }
  33. }

對於 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() 方法。

實踐驗證

下麵用一個實體來真實看看看上面執行的邏輯,畢竟理論是不能缺少實踐的:

  1. public class lifeCycleBean implements BeanNameAware,BeanFactoryAware,BeanClassLoaderAware,BeanPostProcessor,
  2.        InitializingBean,DisposableBean {
  3.  
  4.    private String test;
  5.  
  6.    public String getTest() {
  7.        return test;
  8.    }
  9.  
  10.    public void setTest(String test) {
  11.        System.out.println("屬性註入....");
  12.        this.test = test;
  13.    }
  14.  
  15.    public lifeCycleBean(){
  16.        System.out.println("建構式呼叫...");
  17.    }
  18.  
  19.    public void display(){
  20.        System.out.println("方法呼叫...");
  21.    }
  22.  
  23.    @Override
  24.    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
  25.        System.out.println("BeanFactoryAware 被呼叫...");
  26.    }
  27.  
  28.    @Override
  29.    public void setBeanName(String name) {
  30.        System.out.println("BeanNameAware 被呼叫...");
  31.    }
  32.  
  33.    @Override
  34.    public void setBeanClassLoader(ClassLoader classLoader) {
  35.        System.out.println("BeanClassLoaderAware 被呼叫...");
  36.    }
  37.  
  38.    @Override
  39.    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  40.        System.out.println("BeanPostProcessor postProcessBeforeInitialization 被呼叫...");
  41.        return bean;
  42.    }
  43.  
  44.    @Override
  45.    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  46.        System.out.println("BeanPostProcessor postProcessAfterInitialization 被呼叫...");
  47.        return bean;
  48.    }
  49.  
  50.    @Override
  51.    public void destroy() throws Exception {
  52.        System.out.println("DisposableBean destroy 被調動...");
  53.    }
  54.  
  55.    @Override
  56.    public void afterPropertiesSet() throws Exception {
  57.        System.out.println("InitializingBean afterPropertiesSet 被調動...");
  58.    }
  59.  
  60.    public void initMethod(){
  61.        System.out.println("init-method 被呼叫...");
  62.    }
  63.  
  64.    public void destroyMethdo(){
  65.        System.out.println("destroy-method 被呼叫...");
  66.    }
  67.  
  68. }

lifeCycleBean 繼承了 BeanNameAware , BeanFactoryAware , BeanClassLoaderAware , BeanPostProcessor ,InitializingBean , DisposableBean 六個介面,同時定義了一個 test 屬性用於驗證屬性註入和提供一個 display() 用於模擬呼叫。 配置如下:

  1. id="lifeCycle" class="org.springframework.core.test.lifeCycleBean"
  2.        init-method="initMethod" destroy-method="destroyMethdo">
  3.     name="test" value="test"/>

配置 init-method 和 destroy-method。測試方法如下:

  1. // BeanFactory 容器一定要呼叫該方法進行 BeanPostProcessor 註冊
  2. factory.addBeanPostProcessor(new lifeCycleBean());
  3.  
  4. lifeCycleBean lifeCycleBean = (lifeCycleBean) factory.getBean("lifeCycle");
  5. lifeCycleBean.display();
  6.  
  7. System.out.println("方法呼叫完成,容器開始關閉....");
  8. // 關閉容器
  9. factory.destroySingletons();

執行結果:

  1. 建構式呼叫...
  2. 建構式呼叫...
  3. 屬性註入....
  4. BeanNameAware 被呼叫...
  5. BeanClassLoaderAware 被呼叫...
  6. BeanFactoryAware 被呼叫...
  7. BeanPostProcessor postProcessBeforeInitialization 被呼叫...
  8. InitializingBean afterPropertiesSet 被調動...
  9. init-method 被呼叫...
  10. BeanPostProcessor postProcessAfterInitialization 被呼叫...
  11. 方法呼叫...
  12. 方法呼叫完成,容器開始關閉....
  13. DisposableBean destroy 被調動...
  14. destroy-method 被呼叫...

有兩個建構式呼叫是因為要註入一個 BeanPostProcessor(你也可以另外提供一個 BeanPostProcessor 實體)。

根據執行的結果已經上面的分析,我們就可以對 Spring Bean 的宣告週期過程如下(方法級別):

  1. Spring 容器根據實體化策略對 Bean 進行實體化。
  2. 實體化完成後,如果該 bean 設定了一些屬性的話,則利用 set 方法設定一些屬性。
  3. 如果該 Bean 實現了 BeanNameAware 介面,則呼叫 setBeanName() 方法。
  4. 如果該 bean 實現了 BeanClassLoaderAware 介面,則呼叫 setBeanClassLoader() 方法。
  5. 如果該 bean 實現了 BeanFactoryAware介面,則呼叫 setBeanFactory() 方法。
  6. 如果該容器註冊了 BeanPostProcessor,則會呼叫 postProcessBeforeInitialization() 方法完成 bean 前置處理
  7. 如果該 bean 實現了 InitializingBean 介面,則呼叫 。 afterPropertiesSet() 方法。
  8. 如果該 bean 配置了 init-method 方法,則呼叫 init-method 指定的方法。
  9. 初始化完成後,如果該容器註冊了 BeanPostProcessor 則會呼叫 postProcessAfterInitialization() 方法完成 bean 的後置處理。
  10. 物件完成初始化,開始方法呼叫。
  11. 在容器進行關閉之前,如果該 bean 實現了 DisposableBean 介面,則呼叫 destroy() 方法。
  12. 在容器進行關閉之前,如果該 bean 配置了 destroy-mehod,則呼叫其指定的方法。
  13. 到這裡一個 bean 也就完成了它的一生。

贊(0)

分享創造快樂