1. 程式人生 > 程式設計 >Spring原始碼學習(二)哎呦,按菜譜做菜與AbstractAutowireCapableBeanFactory.createBean流程差不多

Spring原始碼學習(二)哎呦,按菜譜做菜與AbstractAutowireCapableBeanFactory.createBean流程差不多

記得跟老婆談戀愛時,有一天心血來潮給老婆做飯,按照菜譜一步一步的做,結果差點把廚房燒了!!!

這事至今老婆還記得。

入口

上一篇說了,AbstractBeanFactory.getBean的主流程,今天來說下其中的createBean方法,程式入口如下:

/**.
 * 這個類的核心方法,建立一個bean例項, 填充bean例項,執行後處理等
 * @see #doCreateBean  詳見doCreateBean
 */
@Override
protected Object createBean(String beanName,RootBeanDefinition mbd,@Nullable Object[] args)
      throws BeanCreationException {
	  //.....
}
複製程式碼

根據註釋核心邏輯在doCreateBean 中,下面咱們就聊聊doCreateBean.

doCreateBean 邏輯說明

主流程 類比按菜譜做菜
1. 例項化Bean BeanDefinition->BeanWrapper(如果是單例,先嚐試從快取中清楚並獲取BeanWrapper) 找到菜譜,先嚐試從收藏中獲取
2.處理MergedBeanDefinitionPostProcessors 融合老婆的要求,到菜譜中。例如:少放鹽
3.允許提前暴露ObjectFactory,解決迴圈引用問題(必須滿足:單例&&允許迴圈引用&&對應的bean在建立) 提前告訴老婆菜的大概味道,方便美麗的她準備飲料,也可以方便自己提前找出盤子
4.填充屬性 炒菜
5.執行 init-method方法 試吃
6.有其他bean依賴當前完整bean(必須填充完屬性),移除這些bean,有不能移除的丟擲異常 發現之前準備的盤子太小了,換個新的。
7.註冊DisposableBean介面或destroy—method 做個好男人,吃完飯記得刷碗

原始碼註釋在最後!!

一圖勝千言(時序圖)

doCreate流程
doCreate流程

補充說明

1.例項化Bean BeanDefinition->BeanWrapper流程

BeanDerfinition已經上篇已經做過介紹(

上一篇blog的傳送門),下面說下BeanWrapper。 org.springframework.beans,BeanWrapper的註釋翻譯如下:

BeanWrapper是Spring中底層javaBean的核心介面 通常不直接使用,而是被BeanFactory和DataBinder使用 它提供對標準javabean的分析和操作的功能。 可以獲取,設定屬性值,獲取屬性值的descriptors,用於查詢可讀,可寫的屬性。 支援無限層巢狀屬性。 BeanWrapper預設不支援對屬性的舊值進行編輯,這樣可以避免getter被呼叫時產生的副作用 設定 extractOldValueForEditor 為true,可以開啟對舊屬性值進行編輯。

知道了BeanWrapper就不難猜出BeanDefinition->BeanWrapper的大致邏輯了

BeanDefinition->BeanWrapper 就是將bean的元資料,轉換為Bean,但是Bean沒有對屬性進行賦值。
-- 溫安適 胡亂總結於20190921

createBeanInstance的原始碼:

protected BeanWrapper createBeanInstance(String beanName,@Nullable Object[] args) {
   // 確認 bean的class,在這時已被解析了
   Class<?> beanClass = resolveBeanClass(mbd,beanName);
   if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
      throw new BeanCreationException(mbd.getResourceDescription(),beanName,"Bean class isn't public,and non-public access not allowed: " + beanClass.getName());
   }
   Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
   if (instanceSupplier != null) {
      return obtainFromSupplier(instanceSupplier,beanName);
   }

   if (mbd.getFactoryMethodName() != null) {
      return instantiateUsingFactoryMethod(beanName,mbd,args);
   }
   // 判斷 是否重新建立同一bean時
   boolean resolved = false;
   boolean autowireNecessary = false;
   if (args == null) {
      synchronized (mbd.constructorArgumentLock) {
         if (mbd.resolvedConstructorOrFactoryMethod != null) {
            resolved = true;
            autowireNecessary = mbd.constructorArgumentsResolved;
         }
      }
   }
   if (resolved) {
      if (autowireNecessary) {
         return autowireConstructor(beanName,null,null);
      }
      else {
         return instantiateBean(beanName,mbd);
      }
   }
   // 自動注入的構造器的,候選者
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass,beanName);
   if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
      return autowireConstructor(beanName,ctors,args);
   }
   // 獲得首選的構造器,可能是預設構造器
   ctors = mbd.getPreferredConstructors();
   if (ctors != null) {
      return autowireConstructor(beanName,null);
   }
   // 沒有特別處理簡單實用,無參構造器
   return instantiateBean(beanName,mbd);
}


複製程式碼

我們可以看到,這個createBeanInstance ,有3種處理方式

  • instantiateUsingFactoryMethod 按照工廠方法進行例項化
  • autowireConstructor 建構函式注入, 構造器選擇大致邏輯:
    1. 如果僅有預設構造器,沒有指定引數,BeanDefinition中也沒有構造器引數,使用預設構造方法。
    2. 如果有多個建構函式,對多個構造方法按照public優於private,同可見性的情況下,引數多的優於引數少的進行排序。一般採用寬鬆匹配模式(可以設定為嚴格匹配),優先選擇引數原值型別匹配的,引數轉換後型別匹配的次之,最後是都不匹配的。如果出現匹配度一致性的,選擇第一個匹配的構造器。
  • instantiateBean 例項化bean,使用預設構造方法。

咱們看下instantiateBean 的邏輯

一般按照反射生成例項的方式如下方式:

public static void main(String[] args) throws Exception{
      Class<CouponApplicationTests> clazz=CouponApplicationTests.class;
      clazz.newInstance();
      Constructor constructorToUse = clazz.getDeclaredConstructor();
      constructorToUse.newInstance()
}


複製程式碼

Spring是如何實現的基本類似。查閱instantiateBean的原始碼發現,其核心邏輯委託給InstantiationStrategy的instantiate方法。Spring5.X的預設InstantiationStrategy 是CglibSubclassingInstantiationStrategy,而instantiate的實現並不在CglibSubclassingInstantiationStrategy中而是在,其父類SimpleInstantiationStrategy中。如下:

@Override
public Object instantiate(RootBeanDefinition bd,@Nullable String beanName,BeanFactory owner) {
   // Don't override the class with CGLIB if no overrides.
   // 如果沒有override方法, 即沒有使用CGLIB重寫
   if (!bd.hasMethodOverrides()) {
      Constructor<?> constructorToUse;
      synchronized (bd.constructorArgumentLock) {
         constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
         if (constructorToUse == null) {
            final Class<?> clazz = bd.getBeanClass();
            if (clazz.isInterface()) {
               throw new BeanInstantiationException(clazz,"Specified class is an interface");
            }
            try {
               if (System.getSecurityManager() != null) {
                  constructorToUse = AccessController.doPrivileged(
                        (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
               }
               else {
                  constructorToUse = clazz.getDeclaredConstructor();
               }
               bd.resolvedConstructorOrFactoryMethod = constructorToUse;
            }
            catch (Throwable ex) {
               throw new BeanInstantiationException(clazz,"No default constructor found",ex);
            }
         }
      }
	  //就是執行constructorToUse.newInstance()
      return BeanUtils.instantiateClass(constructorToUse);
   }
   else {
      // Must generate CGLIB subclass.
      return instantiateWithMethodInjection(bd,owner);
   }
}


複製程式碼

如果沒有MethodOverride,其大致邏輯,跟咱們寫的差不多。 但是如果有MethodOverride則不同。
看到這裡的時候我很好奇hasMethodOverrides和instantiateWithMethodInjection,看名稱我猜測是對@Override註解進行處理,但是看了原始碼這個MethodOverrides是指replace-method和lookup-method,這個2配置,我沒有使用過,查詢了原始碼與網上的blog。簡單說就是在這裡利用Cglib實現方法注入。 對lookup-method和replace-method的說明您可以查閱

2.迴圈引用是什麼?Spring 如何解決的?

什麼是迴圈引用,舉個列子:BeanA引用BeanB,BeanB也引用BeanA

@Component
class BeanA{
   @Autowired
   BeanB beanB;
}
@Component
class BeanB{
   @Autowired
   BeanA beanA;
}
複製程式碼

Spring只能解決單例型別的迴圈,其解決幫扶就是提前暴露ObjectFactory,將未填充完屬性的bean提前暴露出來。 流程圖如下:

Spring解決迴圈引用

囉嗦幾句

寫blog比看原始碼要費勁的多,我總是嘗試使用生活中的例子比喻主流程,再加一點的補充說明。 以做菜比喻Spring原始碼,感覺自己原始碼學雖然不透徹,但是胃口好多了。

做菜喻原始碼
輪廓漸清晰
理解未透徹
肚子叫呱呱
美餐一頓去
下回咱再聊

populateBean下一篇解決 預計10月2號前

原始碼註釋

/**
 * Actually create the specified bean. Pre-creation processing has already happened
 * at this point,e.g. checking {@code postProcessBeforeInstantiation} callbacks.
 * <p>Differentiates between default bean instantiation,use of a
 * factory method,and autowiring a constructor.
 * 建立指定的Bean,此時建立前預處理已經執行了(檢視postProcessBeforeInstantiation)。
 * 區分預設bean例項化,工廠方法,並自動注入建構函式。
 * @param beanName the name of the bean
 * @param mbd the merged bean definition for the bean
 * @param args explicit arguments to use for constructor or factory method invocation
 * @return a new instance of the bean
 * @throws BeanCreationException if the bean could not be created
 * @see #instantiateBean
 * @see #instantiateUsingFactoryMethod
 * @see #autowireConstructor
 */
protected Object doCreateBean(final String beanName,final RootBeanDefinition mbd,final @Nullable Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   // 1.例項化bean
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      //Cache of unfinished FactoryBean instances 從FactoryBean name to BeanWrapper.
      //移除未完成的bean的快取中的例項
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      //建立Bean例項 將BeanDefinition替換成BeanWrapper
      instanceWrapper = createBeanInstance(beanName,args);
   }
   final Object bean = instanceWrapper.getWrappedInstance();
   Class<?> beanType = instanceWrapper.getWrappedClass();
   if (beanType != NullBean.class) {
      mbd.resolvedTargetType = beanType;
   }
   // Allow post-processors to modify the merged bean definition.
   synchronized (mbd.postProcessingLock) {
      //Bean
      if (!mbd.postProcessed) {
         try {
            //@AutoWired註解在這裡,應用
            //2.處理MergedBeanDefinitionPostProcessors
            applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName);
         }
         catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(),"Post-processing of merged bean definition failed",ex);
         }
         mbd.postProcessed = true;
      }
   }
   // Eagerly cache singletons to be able to resolve circular references
   // even when triggered by lifecycle interfaces like BeanFactoryAware.
   // 3.允許提前暴露ObjectFactory,解決迴圈引用問題
   //  單例,允許迴圈引用,對應的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");
      }
      addSingletonFactory(beanName,() -> getEarlyBeanReference(beanName,bean));
   }

   // Initialize the bean instance.
   // 例項化bean
   Object exposedObject = bean;
   try {
      //4.填充屬性
      populateBean(beanName,instanceWrapper);
      //5.執行 init-method方法
      exposedObject = initializeBean(beanName,exposedObject,mbd);
   }
   catch (Throwable ex) {
      if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
         throw (BeanCreationException) ex;
      }
      else {
         throw new BeanCreationException(
               mbd.getResourceDescription(),"Initialization of bean failed",ex);
      }
   }
   //6.有其他bean依賴當前完整bean(填充過屬性),移除這些bean,有不能移除的丟擲異常
   if (earlySingletonExposure) {
      Object earlySingletonReference = getSingleton(beanName,false);
      if (earlySingletonReference != null) {
         if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
         }
         else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            //不允許注入未被完全裝載的bean,並且有其他Bean依賴當前這個Bean
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
               //移除依賴於當前Bean的其他bean
               if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                  actualDependentBeans.add(dependentBean);
               }
            }
            if (!actualDependentBeans.isEmpty()) {
               //還有其他bean,依賴於當前Bean,未被移除
               throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +
                     StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                     "] in its raw version as part of a circular reference,but has eventually been " +
                     "wrapped. This means that said other beans do not use the final version of the " +
                     "bean. This is often the result of over-eager type matching - consider using " +
                     "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off,for example.");
            }
         }
      }
   }
   // Register bean as disposable.
   try {
      //7.如果實現了 DisposableBean介面或者提供了destroy—method 在這裡註冊
      registerDisposableBeanIfNecessary(beanName,bean,mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(),"Invalid destruction signature",ex);
   }
   return exposedObject;
   }


複製程式碼