Spring 獲取單例流程(三)

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

  • 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 是一個單例,app

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 究竟幹啥子了post

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; } 複製代碼

上面的流程大體就是this

  • 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 循環依賴 這篇文章

此次必定?
此次必定?
羣聊
羣聊
相關文章
相關標籤/搜索