聊透 Spring bean 的生命週期

作者:貳師兄的屠宰場

原文:https://juejin.cn/post/7155884227714613285

在對於 Spring 的所有解讀中,Bean 的生命週期都可謂是重中之重,甚至還有人稱 Spring 就是個管理 Bean 的容器。Bean 的生命週期之所以這麼重要,被反覆提及,是因爲 Spring 的核心能力,比如對象創建(IOC)屬性注入(DI)初始化方法的調用代理對象的生成(AOP)等功能的實現,都是在 bean 的生命週期中完成的。清楚了 bean 的生命週期,我們才能知道 Spring 的神奇魔法究竟是什麼,是怎麼一步步賦能,讓原本普通的 java 對象,最終變成擁有超能力的 bean 的。

1. bean 的生命週期

Spring 的生命週期大致分爲:創建 -> 屬性填充 -> 初始化bean -> 使用 -> 銷燬 幾個核心階段。我們先來簡單瞭解一下這些階段所做的事情:

當然,bean 的生命週期中還包括其他的流程,比如暴露工廠對象等,只是相對而言都是爲其他功能做伏筆和準備的,再講到對應功能時,我們在做詳細分析。

1.1 創建 bean

對象的創建是 bean 生命週期的第一步,畢竟要先有 1 纔能有 0 嘛。創建對象的方式有很多,比如 new反射clone等等,Spring 是怎麼創建對象的呢?絕大多數情況下,Spring是通過反射來創建對象的,不過如果我們提供了Supplier或者工廠方法,Spring 也會直接使用我們提供的創建方式。

我們從源碼出發,看一下 Spring 是如何選擇創建方式的:

// 源碼位於 AbstractAutowireCapableBeanFactory.java
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
   // 再次解析BeanDefinition的class,確保class已經被解析
   Class<?> beanClass = resolveBeanClass(mbd, beanName);

   // 1: 如果提供了Supplier,通過Supplier產生對象
   Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
   if (instanceSupplier != null) {
      return obtainFromSupplier(instanceSupplier, beanName);
   }

   // 2: 如果有工廠方法,使用工廠方法產生對象
   // 在@Configration配置@Bean的方法,也會被解析爲FactoryMethod
   if (mbd.getFactoryMethodName() != null) {
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }
   //...省略部分代碼
   // 3: 推斷構造方法
   // 3.1 執行後置處理器,獲取候選構造方法
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
   // 3.2 需要自動注入的情況
   if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
      return autowireConstructor(beanName, mbd, ctors, args);
   }

   // 3.3 默認使用沒有參數的構造方法
   return instantiateBean(beanName, mbd);
}

經過我們跟蹤源碼,發現 Spring 推斷創建方式還是比較聰明的,具體邏輯是:

  1. 先判斷是否提供了 Supplier,如果提供,則通過 Supplier 產生對象。

  2. 再判斷是否提供工廠方法,如果提供,則使用工廠方法產生對象。

  3. 如果都沒提供,需要進行構造方法的推斷,邏輯爲:

創建 bean

關於創建 bean 時,具體如何選擇構造方法的,本文我們不詳細展開。因爲本文主旨在於分析 bean 的生命週期,我們只需要簡單知道 Spring 會選擇一個構造方法,然後通過反射創建出對象即可。其實在閱讀 Spring 源碼的時候,小夥伴們也一定要學會抓大放小,重點關注核心流程,細枝末節的地方可以先戰術性忽略,後續有需要時再回過頭分析也不遲,千萬不要陷進去,迷失了方向。

這裏給感興趣的小夥伴附上一張流程圖,感興趣的小夥伴也可以留言,後續我們也可以單獨分析。

創建 bean 時推斷構造方法的流程分析

1.2 merged BeanDefinition

本階段是 Spring 提供的一個拓展點,通過MergedBeanDefinitionPostProcessor類型的後置處理器,可以對 bean 對應的BeanDefinition進行修改。Spring 自身也充分利用該拓展點,做了很多初始化操作 (並沒有修改 BeanDefinition),比如查找標註了@Autowired、 @Resource@PostConstruct@PreDestory 的屬性和方法,方便後續進行屬性注入和初始化回調。當然,我們也可以自定義實現,用來修改 BeanDefinition 信息或者我們需要的初始化操作,感興趣的小夥伴可以自行試一下哦。

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
   for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof MergedBeanDefinitionPostProcessor) {
         MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
         bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
      }
   }
}

merged 階段的查找 merged BeanDefinition

1.3 暴露工廠對象

本階段主要是將早期 bean 對象提前放入到三級緩存singletonFactories中,爲循環依賴做支持。在後續進行屬性填充時,如果發生循環依賴,可以從三級緩存中通過getObject()獲取該 bean,完成循環依賴場景下的自動注入。

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   if (logger.isTraceEnabled()) {
      logger.trace("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
   }
   // 做循環依賴的支持 將早期實例化bean的ObjectFactory,添加到單例工廠(三級緩存)中
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

該階段完全是爲了支撐循環依賴的,是 Spring 爲解決循環依賴埋的伏筆,在 Bean 的生命週期中完全可以忽略。這裏爲了完整性,和小夥伴們簡單提及一下。

暴露工廠對象

1.4 屬性填充

本階段完成了 Spring 的核心功能之一:依賴注入,包括自動注入@Autowired注入@Resource注入等。Spring 會根據 bean 的注入模型 (默認不自動注入),選擇根據名稱自動注入還是根據類型自動注入。然後調用InstantiationAwareBeanPostProcessor#postProcessProperties()完成 @Autowired 和 @Resource 的屬性注入。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
   // 省略部分代碼
    
   // 獲取bean的注入類型
   int resolvedAutowireMode = mbd.getResolvedAutowireMode();
   // 1: 自動注入
   if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
      MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
      // Add property values based on autowire by name if applicable.
      if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
         // 根據名稱注入
         autowireByName(beanName, mbd, bw, newPvs);
      }
      // Add property values based on autowire by type if applicable.
      if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
         // 根據類型注入
         autowireByType(beanName, mbd, bw, newPvs);
      }
      pvs = newPvs;
   }


   // 2: 調用BeanPostProcessor,完成@Autowired @Resource屬性填充
   PropertyDescriptor[] filteredPds = null;
   if (hasInstAwareBpps) {
      if (pvs == null) {
         pvs = mbd.getPropertyValues();
      }
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;

            // 重點: 完成@Autowired @Resource屬性填充
            PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
               if (filteredPds == null) {
                  // 需要注入的屬性,會過濾掉Aware接口包含的屬性(通過ignoreDependencyInterface添加)
                  filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
               }
               pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
               if (pvsToUse == null) {
                  return;
               }
            }
            pvs = pvsToUse;
         }
      }
   }
  
    // 3: 依賴檢查
   if (needsDepCheck) {
      if (filteredPds == null) {
         filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
      }
      checkDependencies(beanName, mbd, filteredPds, pvs);
   }
   // 4: 將屬性應用到bean中
   if (pvs != null) {
      applyPropertyValues(beanName, mbd, bw, pvs);
   }
}

屬性填充詳細屬性填充

1.5 初始化 bean

該階段主要做 bean 的初始化操作,包括:回調Aware接口回調初始化方法生成代理對象等。

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
  // 1: 回調Aware接口中的方法
  // 完成Aware方法的回調(BeanNameAware,BeanClassLoaderAware,BeanFactoryAware)
  invokeAwareMethods(beanName, bean);

   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
      // 2: 調用before...方法
      // ApplicationContextAwareProcessor: 其他Aware方法的回調
      // InitDestroyAnnotationBeanPostProcessor: @PostConstruct方法的回調
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }

   try {
      // 3: 完成xml版本和@bean(initMethod)的init方法回調
      invokeInitMethods(beanName, wrappedBean, mbd);
   }

   // 4: 調用after方法
   // 重點: AOP生成代理對象
   if (mbd == null || !mbd.isSynthetic()) {
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }

   return wrappedBean;
}

初始化 bean

在初始化完成後,bean 會被放到單例池中,正式開始自己的使命:爲項目服務,比如接收 http 請求,進行 CRUD 等等。後續有使用到該 bean 的地方,也是直接從單例池中獲取,不會再次創建 bean(僅單例的哦)。

2. bean 的來龍去脈

2.1 bean 的掃描階段

現在我們已經知道 Spring bean 是如何創建的了,那什麼時候創建這些 bean 呢,是遵循懶加載的思想,在實際使用的時候在創建嗎?

其實不是的,因爲 bean 之間的複雜關係和生命週期的原因,Spring 在容器啓動的時候,就會實例化這些 bean,然後放到單例池中,即用即取。並且在創建前、創建中、創建後都會做很多檢查,確保創建的 bean 是符合要求的,這些我們就不贅述了。

言歸正傳,細心的你一定發現,創建 bean 時主要是從RootBeanDefinition mbd這個參數獲取 bean 的相關信息的,其實這就是大名鼎鼎的BeanDefinition,其中封裝了關於 bean 的元數據信息,關於 BeanDefinition,後續我們會單獨講解,這裏我們先理解爲 bean 的元數據信息即可。那麼這些元數據信息是什麼時候解析的呢?

這就要提到 Spring 的類掃描了,其大致流程是:通過ASM字節碼技術掃描所有的類 -> 找出需要Sp加了@Compont註解的(簡單理解) -> 封裝成BeanDefinition -> 存放到集合中。後續再實例化 bean 的時候,就可以遍歷這個集合,獲取到BeanDefinition,然後進行 bean 的創建了。

09_掃描階段. png

關於處理類掃描的ConfigurationClassPostProcessor後置處理器以及ConfigurationClassParserComponentScanAnnotationParser掃描器的具體細節,後續我們單獨講解,和本章節關係不大,我們先簡單理解即可。

2.2 實例化後回調

在前面的章節我們分析過:在容器中的 bean 實例化,放到單例池中之後,bean 在創建階段的生命週期就正式完成,進入使用中階段,開啓對完服務之路。

確實,這就是創建 bean 的全過程,如果有小夥伴看過筆者之前的聊 Spring 事件的那篇文章會發現對於@EventListener處理器的識別註冊,是在afterSingletonsInstantiated階段完成的。

其實這裏也是一個拓展點,我們完全可以實現SmartInitializingSingleton#afterSingletonsInstantiated(),在 bean 初始化完成後會回調該方法,進而觸發我們自己的業務邏輯,故這裏我們單獨說一下。不清楚的小夥伴請移步先去了解一下哦。

初始化後回調

2.3 bean 的銷燬階段

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
   // ...省略代碼
   try {
      // 爲bean註冊DisposableBean,在容器關閉時,調用destory()
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }

   return exposedObject;
}

在創建 bean 的時候,會判斷如果 bean 是DisposableBeanAutoCloseable的子類,或者有 destroy-method等,會註冊爲可銷燬的 bean,在容器關閉時,調用對應的方法進行 bean 的銷燬。

bean 的銷燬

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/GU0nlvaY5eg74rLefYhWsQ