Spring 獲取單例流程(三)

讀完這篇文章你將會收穫到

  • Spring何時將 bean加入到第三級緩存和第一級緩存中
  • Spring何時回調各種 Aware接口、BeanPostProcessorInitializingBean

相關文章

概述

上兩篇文章 Spring 獲取單例流程(一)Spring 獲取單例流程(二) 介紹了 getBean前面的流程,今天最後的收尾,把後面的流程繼續一起學習下

源碼分析

// 我依賴的大哥都好了
// Create bean instance.
if (mbd.isSingleton()) {

   sharedInstance = getSingleton(beanName, () -> {
      try {
         return createBean(beanName, mbd, args);
      } catch (BeansException ex) {
         // 從三級緩存中移除這個 beanName  因爲它可能被放進去了 因爲放進去三級緩存可以解決 setter 的循環依賴
         destroySingleton(beanName);
         throw ex;
      }
   });

   bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} 

如果我們要創建的 bean是一個單例,

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {

   synchronized (this.singletonObjects) {
      // 看看第一級緩存中有沒有
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {

         // 將 beanName 加入到 singletonsCurrentlyInCreation 中,代表它正在創建中
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;

         try {
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         } catch (IllegalStateException ex) {
            throw ex;
         } catch (BeanCreationException ex) {
            
            throw ex;
         } finally {
            // singletonsCurrentlyInCreation 從這裏面移除掉
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
            // 加入緩存中
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}

刪減了部分不重要的代碼,我們大致來看看其流程

  1. 獲取同步鎖,然後判斷第一級緩存是否已經存在這個 bean
  2. 如果不存在則將 beanName加入到 singletonsCurrentlyInCreation中,代表它正在創建中
  3. 然後調用參數的 ObjectFactorygetObject方法獲得一個 bean
  4. 最後將其從 singletonsCurrentlyInCreation中移除、代表其已經創建完成了
  5. 最後將其加入到第一級緩存中、從第二級和第三級緩存中移除掉

全篇完結.終 !!!

其實真正的祕密藏身在參數的 ObjectFactory中,從上面的流程中可以宏觀的知道 Spring創建 bean的一個流程

現在我們在看看參數的 ObjectFactory究竟幹啥子了

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {

   ...........
   ...........

   try {
      // 真正 處理邏輯
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      if (logger.isTraceEnabled()) {
         logger.trace("Finished creating instance of bean '" + beanName + "'");
      }
      return beanInstance;
   } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
     
      throw ex;
   } catch (Throwable ex) {
      throw new BeanCreationException(xxxx);
   }
}

幹活的還是 do開頭的大佬

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      // 根據指定 bean 使用對應的策略創建新的實例、如工廠方法、構造函數自動注入、簡單初始化
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   final Object bean = instanceWrapper.getWrappedInstance();
   Class<?> beanType = instanceWrapper.getWrappedClass();
   
  ..........
  .........
   // 是否需要提前曝光、用來解決循環依賴的問題
   // 是單例&允許循環依賴&正在創建中
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   if (earlySingletonExposure) {
      // 爲了避免後期循環依賴、可以在 bean 初始化前將創建實例的ObjectFactory 加入工廠
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      // 填充屬性
      populateBean(beanName, mbd, instanceWrapper);
      // 調用初始方法
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   } catch (Throwable ex) {
      .........
   }

   ........
   .......
  

   return exposedObject;
}

上面的流程大致就是

  • createBeanInstance這個方法根據你的配置以及你的 bean的情況選擇出一種創建 bean的方法、可能是工廠方法、可能是某個構造函數、可能是默認的構造函數。這裏包含了當一個構造函數的參數是另一個 bean的時候、它會通過 getBean的方法獲取這個參數的 bean

  • 然後將創建好的 bean加入到第三級緩存中,默認設置我們是允許循環依賴的

  • populateBean方法就是我們填充屬性了、如果你依賴的其他 Spring的其他 bean是通過這種方式注入的話(autowireByName``````autowireByType)、就是在這一步注入的了,他獲取其他 bean也是通過 getBean的方式獲取

    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
        .........
        .........
      
          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);
          }
        .........
        .........
    }
    
  • initializeBean則是調用我們的各種回調接口、Aware類型的、BeanPostProcessorInitializingBean、自定義初始化函數

    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
       if (System.getSecurityManager() != null) {
          AccessController.doPrivileged((PrivilegedAction<Object>) () -> {// 調用各種 Aware 接口
             invokeAwareMethods(beanName, bean);
             return null;
          }, getAccessControlContext());
       } else {
           // 調用各種 Aware 接口
          invokeAwareMethods(beanName, bean);
       }
    
       Object wrappedBean = bean;
       if (mbd == null || !mbd.isSynthetic()) {
           // 調用 BeanPostProcessor postProcessBeforeInitialization
          wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
       }
    
       try {
           // 調用 InitializingBean 、自定義的初始化方法
          invokeInitMethods(beanName, wrappedBean, mbd);
       } catch (Throwable ex) {
          throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
       }
       if (mbd == null || !mbd.isSynthetic()) {
           //  調用 BeanPostProcessor postProcessAfterInitialization
          wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
       }
       return wrappedBean;
    }
    

    其實整體的流程就差不多了

總結

  • 根據參數中的 name找出對應的 beanName、無論這個 name是別名或者是一個 factoryBeanbeanName
  • 查看緩存中是否包含這個 beanName對象
    • 先從一級緩存 singletonObjects中看看有沒有
    • 然後從二級緩存 earlySingletonObjects
    • 都沒有的話再從三級緩存 singletonFactories中看看有沒有
  • 如果緩存中有 bean、那麼我們還是需要處理一下這個 bean
    • 如果 Spring緩存中返回的 beanfactoryBean、而用戶也想要的是一個 beanFactory(參數 name中的前綴是 &)、那麼我們直接返回
    • 如果 Spring緩存中返回的 bean是普通的 bean、而用戶也想要的是一個普通的 bean、那麼就直接返回
    • 如果 Spring緩存中返回的 bean是一個 factoryBean、而用戶想要的是一個普通的 bean、那麼我們就要從 factoryBean中獲取這個 bean
    • 而從 factoryBean中獲取這個 bean的過程中、需要調用到前置處理、後置處理和我們常用的接口回調 BeanPostProcessor
  • 如果緩存中沒有 bean、則判斷是否是 prototype類型並且循環依賴
  • 如果沒有則嘗試能否在父容器中找到該 bean
  • 如果父容器也沒有則獲取該 beanName對應的 beanDefinition找出其依賴的 beanName
  • 判斷該 beanName與 依賴的 beanName是否循環依賴、沒有則註冊其依賴關係並調用 getBean方法去創建依賴的 beanName
  • beanName加入到 singletonsCurrentlyInCreation
  • 根據指定 bean 使用對應的策略創建新的實例、如工廠方法、構造函數、創建一個不完整的 bean
  • 將創建好的 bean 加入到第三級緩存
  • 進行屬性填充、進行各種接口回調
  • 最後將其從 singletonsCurrentlyInCreation中移除、代表其已經創建完成了
  • 最後將其加入到第一級緩存中、從第二級和第三級緩存中移除掉
  • 返回 bean給調用方

其實總體的流程還是不算複雜把、我們也可以從中收穫到一些東西。其實我們最關心也是面試最常問的一個問題就是、Spring 如何解決循環依賴的問題、感興趣的可以看看這篇文章公衆號內的 Spring 循環依賴這篇文章

這次一定?

羣聊

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