Spring原始碼之Bean的載入(四)

神祕傑克發表於2022-06-11

bean 的載入(四)

前一篇文章主要講解了關於 Spring 迴圈依賴相關的問題。本文繼續講解關於 bean 的載入過程之建立 bean。

建立 Bean

我們先進入到 createBean 方法中。

之前文章我們說過在經歷了 resolveBeforeInstantiation 方法後,程式會有兩個選擇,如果建立了代理或者重寫了 InstantiationAwareBeanPostProcessor 中的 postProcessBeforeInstantiation 方法並在方法 postProcessBeforeInstantiation 中改變了 bean,則會直接返回,否則會進行常規 bean 的建立。

// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
//給BeanPostProcessors一個機會來返回代理用於替代真正的例項
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
   return bean;
}

常規 bean 的建立則在 doCreateBean 中。

//程式碼(1)
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
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();
   if (beanType != NullBean.class) {
      // 將解析型別設定為beanType
      mbd.resolvedTargetType = beanType;
   }

   // Allow post-processors to modify the merged bean definition.
   synchronized (mbd.postProcessingLock) {
      if (!mbd.postProcessed) {
         try {
            // 這裡主要是 MergedBeanDefinitionPostProcessor 對@Autowire,@Value這些註解進行處理
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
         } catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                  "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.
   //是否需要提前曝光,單例&&允許迴圈依賴&&當前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加入快取
      // 對bean 再一次依賴引用,主要應用SmartInstantiationAwareBeanPostProcessor
      // 其中我們熟悉的AOP就是在這裡將advice 動態織入,若沒有直接返回bean
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      //對bean進行填充,比如設定屬性,其中可能存在依賴其他bean,則會遞迴初始化依賴的bean
      populateBean(beanName, mbd, instanceWrapper);
      // 進一步初始化Bean
      // 注入 Aware 相關的物件
      // 呼叫 後置處理器 BeanPostProcessor 裡面的postProcessBeforeInitialization方法
      // 呼叫 InitializingBean中的的 afterPropertiesSet()
      // 呼叫 init-method,呼叫相應的init方法
      // 呼叫 後置處理器 BeanPostProcessor 裡面的呼叫實現的postProcessAfterInitialization方法
      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(), beanName, "Initialization of bean failed", ex);
      }
   }

   if (earlySingletonExposure) {
      Object earlySingletonReference = getSingleton(beanName, false);
      // earlySingletonReference 只有在檢測到有迴圈依賴的情況下才會不為空
      if (earlySingletonReference != null) {
         //如果exposedObject 沒有在初始化方法中被改變,也就是沒有被增強
         if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
         } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            // 檢查依賴
            for (String dependentBean : dependentBeans) {
               if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                  actualDependentBeans.add(dependentBean);
               }
            }
            /**
             因為 bean 建立後其所依賴的bean一定是已經建立,
             actualDependentBeans 不為空則表示 當前bean 建立後其依賴的bean 卻沒有全部建立,
             也就是說存在依賴
             */
            if (!actualDependentBeans.isEmpty()) {
               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 {
      // 註冊到 disposableBeans 裡面,以便在銷燬bean 的時候 可以執行指定的相關業務
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   } catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }

   return exposedObject;
}

這個方法比較長,我們先梳理一下思路。

  1. 如果是單例則需要首先清除快取
  2. 例項化 bean,將 BeanDefinition 轉換為 BeanWrapper

    轉換是一個比較複雜的過程,大致如下:

    • 如果存在工廠方法則使用工廠方法進行初始化
    • 一個類有多個建構函式,所以需要根據引數鎖定建構函式並初始化
    • 如果不存在工廠方法也不存在帶引數的建構函式,則使用預設的無參建構函式進行初始化
  3. mergedBeanDefinitionPostProcessor 的應用。bean 合併後的處理,Autowired 註解就是通過此方法實現的預解析
  4. 依賴處理。處理迴圈依賴,上一章已經講過,就是將需要注入的例項放入快取中的 ObjectFactory 來解決
  5. 屬性填充。將所有屬性填充到 bean 的例項中
  6. 迴圈依賴檢查。之前說過,Spring 中只能解決單例的迴圈依賴,對於別的 scope 屬性的 bean,Spring 在這個步驟會檢測是否存在迴圈依賴,如果存在則丟擲異常
  7. 註冊 DisposableBean。如果配置了 destory-method,這裡需要註冊以便在銷燬時呼叫
  8. 完成建立並返回

可以看到步驟非常繁瑣,我們接下來一步步進行了解。

建立 bean 的例項

我們先從 createBeanInstance 方法開始瞭解。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
   // Make sure bean class is actually resolved at this point.
   //確保bean已經被解析
   Class<?> beanClass = resolveBeanClass(mbd, beanName);
   // 如果beanClass 不是public型別,則丟擲異常
   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());
   }
   // 如果存在,就返回一個 callback回撥函式,在 obtainFromSupplier 方法裡呼叫對應的具體方法 ,並轉換成 BeanWrapper 型別
   Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
   if (instanceSupplier != null) {
      return obtainFromSupplier(instanceSupplier, beanName);
   }
   //如果工廠方法不為空則使用工廠方法初始化策略
   if (mbd.getFactoryMethodName() != null) {
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }

   // Shortcut when re-creating the same 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, mbd, null, null);
      } else {
         //使用預設的建構函式
         return instantiateBean(beanName, mbd);
      }
   }

   // Candidate constructors for autowiring?
   //需要根據引數解析建構函式
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
   if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
      //建構函式自動注入
      return autowireConstructor(beanName, mbd, ctors, args);
   }

   // Preferred constructors for default construction?
   // 如果上面沒有解析好對應的建構函式, 這裡看看有沒有指定建構函式
   /**
    具體裡面其實 是 SmartInstantiationAwareBeanPostProcessor , 這個類 繼承了
    InstantiationAwareBeanPostProcessor, 呼叫裡面的determineCandidateConstructors方法來確認有沒有指定的建構函式
    */
   ctors = mbd.getPreferredConstructors();
   if (ctors != null) {
      return autowireConstructor(beanName, mbd, ctors, null);
   }

   // No special handling: simply use no-arg constructor.
   //使用預設的建構函式
   return instantiateBean(beanName, mbd);
}

雖然程式碼中例項化的細節很複雜,但是我們可以梳理出來以下邏輯:

  1. 如果在 RootBeanDefinition 中存在 factoryMethodName 屬性,或者在配置檔案中配置了 factory-method 那麼就會呼叫instantiateUsingFactoryMethod(beanName, mbd, args)方法,根據 RootBeanDefinition 中的配置生成 bean 的例項。
  2. 解析建構函式並進行建構函式初始化,Spring 根據引數以及型別去判斷最終使用哪一個建構函式進行例項化。由於判斷比較消耗效能,所以採用快取機制,如果已經解析過則不需要重複解析,而是從 RootBeanDefinition 中的屬性 resolvedConstructorOrFactoryMethod 快取的值去取,否則需要進行解析,並將解析結果新增到 resolvedConstructorOrFactoryMethod 中。

解析autowireConstructor方法

/**
*   beanName: Bean的名稱
*   mbd:    該bean的BeanDefinition
*   chosenCtors: 該類的構造器陣列
*   explicitArgs:這個方法時通過getBean方法過來的,那麼既然是getBea過來的,那麼在我們getBean的時候除了傳入beanName/beanClass以外,還可以傳入其他引數,如果傳入了其他引數,那麼Spring
*                   則認為這些引數是構造器初始化物件時的構造方法引數列表,而這個其他引數就是此時的explicitArgs.
*/
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
      @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {

    BeanWrapperImpl bw = new BeanWrapperImpl();
    /**
     * 初始化BeanWrapper
     * */
    this.beanFactory.initBeanWrapper(bw);
    /**
     * constructorToUse是我們實際上使用的構造器
     * */
    Constructor<?> constructorToUse = null;
    /**
     * argsHolderToUse用來儲存用到的構造器的引數,下面的argsToUse的值也是從這個argsHolderToUse中取出來的
     */
    ArgumentsHolder argsHolderToUse = null;
    /**
     * 構造方法中使用到的引數列表實際的值
     * */
    Object[] argsToUse = null;
    /**
     * 總結一下這個if和else:
     *  1、判斷是否傳入構造引數值列表,如果傳入則賦值
     *  2、沒有傳入則從快取中去取
     * */
    /**
     * 如果我們getBean的時候傳入了引數,那麼Spring就認為我們希望按照指定的構造引數列表去尋找構造器並例項化物件.
     * 那麼這裡如果不為空則實際上需要使用的構造方法引數列表值就已經確定了
     * */
    if (explicitArgs != null) {
      argsToUse = explicitArgs;
    }
    else {
      /**
       * 這裡乾的事情很簡單,如果這個bean是原型的,那麼說明此方法肯定進入過,那麼也肯定找到過合適的構造方法和構造引數值列表,在找到合適的構造方法和構造引數值列表後會加入到快取裡面去,那麼此處如果不是第一次進入的話,那麼快取裡面已經有了不用再次去獲取.
       * 此處做的工作就是從快取中去獲取已經找到過並存進來的構造方法和構造引數值列表.
       * 還有注意,只有當我們引數中explicitArgs為空的時候,構造器才會被快取
       * */
      Object[] argsToResolve = null;
      synchronized (mbd.constructorArgumentLock) {
        constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
        if (constructorToUse != null && mbd.constructorArgumentsResolved) {
          // Found a cached constructor...
          argsToUse = mbd.resolvedConstructorArguments;
          if (argsToUse == null) {
            argsToResolve = mbd.preparedConstructorArguments;
          }
        }
      }
      // 如果快取中存在則賦值.
      if (argsToResolve != null) {
        argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
      }
    }
    /**
     * 這裡判斷constructorToUse是否為空是因為上面有可能從快取裡面已經拿到了,如果拿到了則不需要進if裡面去尋找,直接去呼叫建立例項化操作了.
     * 這裡重要的是if裡面的程式碼.
     * */
    if (constructorToUse == null) {
      // Need to resolve the constructor.
      boolean autowiring = (chosenCtors != null ||
          mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
      /**
       * 構造器使用的引數
       * */
      ConstructorArgumentValues resolvedValues = null;
      /**
       * 最小引數個數,此值需要用來在迴圈尋找構造器時使用.
       * 如果噹噹前迴圈到的構造器引數值個數小於這個最小值的話,那麼說明就是不合適的,沒必要繼續下去.
       * */
      int minNrOfArgs;
      /**
      * 如果我們getBean的地方傳入構造引數值列表,那麼則最小引數個數就是我們傳入的列表長度
      * */
      if (explicitArgs != null) {
        minNrOfArgs = explicitArgs.length;
      }
      else {
        /**
         * 如果我們沒有傳入構造器引數值列表,那麼則去解析看有沒有配置構造器引數列表,例如如下配置:
         * <bean class="com.dh.aop.package1.Demo2" id="demo2">
         *    <constructor-arg index="0" value="111"></constructor-arg>
         *    <constructor-arg index="1" value="222"></constructor-arg>
         *  </bean>
         *  這個時候,minNrOfArgs的值就是2
         *  如果我們沒有配置構造器引數的話,這個minNrOfArgs的值就是0
         * */
        ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
        resolvedValues = new ConstructorArgumentValues();
        minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
      }

      // Take specified constructors, if any.
      /**
       * 將該方法的引數構造器列表賦值給candidates
       * */
      Constructor<?>[] candidates = chosenCtors;
      /**
       * 如果傳入的構造器列表為空,則通過class物件去拿
       * 如果bd中設定了允許訪問非public的構造器,那麼則獲取所有的構造器,否則獲取public的構造器.
       * 注意這裡isNonPublicAccessAllowed的預設值為true.
       * 如果獲取構造器的時候出錯當然就要拋異常.
       * */
      if (candidates == null) {
        Class<?> beanClass = mbd.getBeanClass();
        try {
          candidates = (mbd.isNonPublicAccessAllowed() ?
              beanClass.getDeclaredConstructors() : beanClass.getConstructors());
        }
        catch (Throwable ex) {
          throw new BeanCreationException(mbd.getResourceDescription(), beanName,
              "Resolution of declared constructors on bean Class [" + beanClass.getName() +
              "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
        }
      }
      /**
       * 對構造器進行排序:
       *  public的大於其他的許可權
       *  如果都是public的,那麼引數越多越靠前.
       *  可以看這個sort方法裡面可以看到的
       * */
      AutowireUtils.sortConstructors(candidates);
      /**
       * 差異變數
       * */
      int minTypeDiffWeight = Integer.MAX_VALUE;
      /**
       * 有歧義的構造器,就是引數數量一致的,這種情況下的構造器就被列為有歧義的.
       * 正常情況下,如果出現有歧義的構造器,那麼就使用第一個,這取決於spring設定的寬鬆模式.
       * 預設為寬鬆,如此的話就預設使用第一個構造器使用
       * 如果設定為嚴格,則會報錯
       * 設定寬鬆/嚴格模式標誌:beanDefinition.setLenientConstructorResolution
       * */
      Set<Constructor<?>> ambiguousConstructors = null;
      LinkedList<UnsatisfiedDependencyException> causes = null;
      /**
       * 下面就是迴圈的拿構造器去校驗判斷選取一個合適的構造器了.在此之前我們總結一下上述程式碼做的事情.
       * 1、定義constructorToUse、argsHolderToUse、argsToUse,這些分別用來存後面實際上需要使用的構造器、構造器引數、值等
       * 2、如果getBean呼叫的時候傳入了構造器引數,那麼argsToUse的值就被賦值為傳入的構造器引數,否則就嘗試從快取裡面去拿constructorToUse和argsToUse
       *    這個快取就是當bean不是原型的時候例項化時找到的合適的構造器等引數,當然如果是第一次進來,或者bean是單例的,那麼此快取中肯定沒有這個bean相關的構造器資料
       * 3、如果快取裡面有,則直接例項化bean後放到wrapper中並return,如果不存在則需要再次進行一些操作
       * 4、在不存在時,首先定義resolvedValues,這個是後續迴圈裡面需要使用到的構造器使用的引數列表,定義minNrOfArgs,這個是最小引數個數,首先如果getBean傳入了構造器引數
       * 那麼此值就是傳入構造引數的長度,否則就嘗試看我們有沒有配置使用某個構造器,如果都沒有,那麼這個值就是0了,這個變數用來後面在迴圈構造器的時候篩選用的
       * 5、然後定義candidates變數,然後將chosenCtors(是前面傳入的構造器列表)賦值過去,如果它為空,那麼則需要去通過class去拿構造器,拿的時候判斷了一手
       * 去拿BeanDefinition中的isNonPublicAccessAllowed,這個isNonPublicAccessAllowed意思為是否允許訪問非public的構造器,如果為true,則去獲取所有的構造器,否則只獲取public的
       * 6、然後對所有的構造器進行排序,規則為public>其他許可權,引數個數多的>引數個數少的,至於為什麼排序這個可能是spring認為引數越多的越科學吧
       * 7、差異變數,這個看迴圈裡面的程式碼才能理解
       * 8、定義ambiguousConstructors為有歧義的構造器,意思就是如果兩個構造器引數一致,那Spring就不知道該去用哪個,這時這兩個構造器就被放入ambiguousConstructors集合中,他們兩個就是有歧義的構造器
       * ================================================================================
       * 下面迴圈裡面需要搞清楚的就是它具體是如何選取到合適的構造器來使用
       * */
      for (Constructor<?> candidate : candidates) {
        /**
         * 拿到當前構造方法的引數class陣列
         * */
        Class<?>[] paramTypes = candidate.getParameterTypes();
        /**
         * 前面說了[constructorToUse]這個變數是當前確定使用的構造器,如果它不為空,那麼說明我們已經確定了使用哪個構造器,那麼就沒必要繼續下去了.
         * 但[argsToUse.length > paramTypes.length]這個就比較難理解.
         * 注意每次迴圈以後argsToUse的值就會改變為那次迴圈的構造器的引數,如果當前拿到的argsToUse引數列表的長度大於當前這個構造器的長度,那麼說明上一次拿到的這個argsToUse比當前的這個更合適(上面也說過,Spring認為引數越多的越科學)
         * 這裡可以注意一下前面sort排序的時候,構造引數個數越多的越靠前,所以這裡敢於用長度判斷後直接break,因為如果上一次迴圈的構造器引數列表為2個,那麼這一次(也就是下一次)的構造引數列表肯定不會比2大,那麼說明對於引數個數而言,上一次的引數個數肯定不會比這一次少,那麼肯定就更合適了唄
         */
        if (constructorToUse != null && argsToUse.length > paramTypes.length) {
          // Already found greedy constructor that can be satisfied ->
          // do not look any further, there are only less greedy constructors left.
          break;
        }
        /**
         * 如果當前構造器的引數數量比最小引數列表數量小的時候,那麼跳過這個構造器.
         * minNrOfArgs的值有兩個地方賦值了:
         *  1、如果我們getBean時傳入了其他引數,那麼其他引數的個數就是minNrOfArgs的值
         *  2、如果我們getBean沒有傳引數,那麼minNrOfArgs的值就是我們配置讓Spring指定使用某些引數的構造器,那麼我們配置的引數列表數量也就是當前的minNrOfArgs
         *  3、如果上述的情況都不存在,那麼minNrOfArgs就是0了,大多數時候都是這種情況,如果都沒配置,那麼就得Spring自己慢慢而不存在此處的篩選了.
         * 所以總結來說此處就是做了一個根據我們自己定義的來篩選的操作
         * */
        if (paramTypes.length < minNrOfArgs) {
          continue;
        }
        /**
         * 儲存構造器需要的引數
         * */
        ArgumentsHolder argsHolder;
        /**
         * 此處resolvedValues不為空則說明getBean時傳入的引數explicitArgs為空的,
         * 因為上面的程式碼是如果explicitArgs不為空則不對resolvedValues賦值,否則就對resolvedValues賦值
         * 此處先看else的程式碼,會更清晰.
         * 如果傳入的引數為空,那麼則會去拿引數了
         * */
        if (resolvedValues != null) {
          try {
            /**
             * 去拿到引數列表的名稱,這裡ConstructorPropertiesChecker.evaluate是這樣做的:
             * 如果構造方法上加入了ConstructorProperties註解,那麼說明我們引數名稱陣列,如果沒有這個註解,那麼次數paramNames為空的
             * */
            String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
            /** 這裡為空則代表我們沒有通過註解去自定義引數名稱,則通過ParameterNameDiscoverer去解析拿到構造器的引數名稱列表 */
            if (paramNames == null) {
              ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
              if (pnd != null) {
                /** 解析拿到引數名稱列表 */
                paramNames = pnd.getParameterNames(candidate);
              }
            }
            /**
             * 此處會去獲取這些引數名稱的引數值,如果是自動注入的就會通過getBean獲取,當前這種構造器注入的情況如果迴圈依賴則會報錯的.
             * 這裡我們只需要知道,此處將構造器需要的引數值拿出來後並封裝到了argsHolder中去.
             * 當然如果你構造器裡面給個Integer的引數,那肯定是會報錯的,因為這裡面會去Spring容器中拿這個Integer,結果呢,肯定是NoSuchBeanDefinitionException了
             * 其餘這裡不用太過於細究,有興趣可以詳細看.
             * */
            argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
                getUserDeclaredConstructor(candidate), autowiring);
          }
          catch (UnsatisfiedDependencyException ex) {
            if (logger.isTraceEnabled()) {
              logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
            }
            // Swallow and try next constructor.
            if (causes == null) {
              causes = new LinkedList<>();
            }
            causes.add(ex);
            continue;
          }
        }
        else {
          /**
           * 到了這個else裡面來,說明getBean呼叫的時候傳入了構造器引數,那麼就說明我們希望按照指定的構造器去初始化Bean.
           * 那麼這裡就需要判斷當前構造器的引數個數是否和我們希望的個數一樣,如果不是,那麼就迴圈去找下一個構造器,
           * 如果和我們希望的是一樣的,那麼就將我們給的引數封裝到argsHolder裡面去
           * */
          // Explicit arguments given -> arguments length must match exactly.
          if (paramTypes.length != explicitArgs.length) {
            continue;
          }
          argsHolder = new ArgumentsHolder(explicitArgs);
        }
        /**
         * 當到達這裡的時候,至此我們拿到了:
         *  1、構造器
         *  2、構造器需要的引數和值
         * 那麼這裡就去結算前面定義的那個差異值.
         * 注意這裡的:isLenientConstructorResolution意思是是否為寬鬆的模式,為true的時候是寬鬆,false的時候是嚴格,預設為true,這個東西前面已經說了.
         * 這個差異值越小越那就說明越合適.
         * 具體差異值如何計算出來的這個可以自行去看裡面的程式碼,argsHolder.getTypeDifferenceWeight(paramTypes)
         * */
        int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
            argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
        // Choose this constructor if it represents the closest match.
        /**
         * 如果本次計算到的差異值比上一次獲取到的差異值小,那麼就需要做這幾件事:
         *  1、設定constructorToUse為當前的這個構造器
         *  2、設定引數和引數值
         *  3、給差異值賦值為當前計算出來的差異值
         *  4、清空有歧義的集合(因為此時我們已經得到了更合適的構造器,所以有歧義的構造器裡面儲存的構造器已經沒有存在的意義了)
         * */
        if (typeDiffWeight < minTypeDiffWeight) {
          constructorToUse = candidate;
          argsHolderToUse = argsHolder;
          argsToUse = argsHolder.arguments;
          minTypeDiffWeight = typeDiffWeight;
          ambiguousConstructors = null;
        }
        /**
         * 如果已經找到過一次構造器,並且當前的差異值和上一次的差異值一致的話,那麼說明這兩個構造器是有歧義的,
         * 那麼就將這兩個構造器放到[有歧義的構造器]集合中去.
         * */
        else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
          if (ambiguousConstructors == null) {
            ambiguousConstructors = new LinkedHashSet<>();
            ambiguousConstructors.add(constructorToUse);
          }
          ambiguousConstructors.add(candidate);
        }
      }
      /**
       * 到達這裡的時候,如果還沒有找到合適的構造器,那麼則直接丟擲異常,這個類沒法例項化
       * */
      if (constructorToUse == null) {
        if (causes != null) {
          UnsatisfiedDependencyException ex = causes.removeLast();
          for (Exception cause : causes) {
            this.beanFactory.onSuppressedException(cause);
          }
          throw ex;
        }
        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Could not resolve matching constructor " +
            "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
      }
      /**
       * 如果存在歧義的構造器集合不為空,並且當前BeanDefinition為嚴格模式,那麼則丟擲異常,只有當BeanDefinition為寬鬆模式時,這種情況才不會拋異常
       * */
      else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Ambiguous constructor matches found in bean '" + beanName + "' " +
            "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
            ambiguousConstructors);
      }
      /**
       * 如果當前getBean沒有傳引數,那麼則將當前的構造器和引數放到快取裡面去,可能Spring認為傳入引數的情況下說不準我們準備怎麼做,所以乾脆我們自己傳入引數的就不快取了
       * */
      if (explicitArgs == null) {
        argsHolderToUse.storeCache(mbd, constructorToUse);
      }
    }
    /**
     * 以下沒什麼好說的,new示例出來,存入beanWrapper中,return回去
     * */
    try {
      final InstantiationStrategy strategy = beanFactory.getInstantiationStrategy();
      Object beanInstance;

      if (System.getSecurityManager() != null) {
        final Constructor<?> ctorToUse = constructorToUse;
        final Object[] argumentsToUse = argsToUse;
        beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
            strategy.instantiate(mbd, beanName, beanFactory, ctorToUse, argumentsToUse),
            beanFactory.getAccessControlContext());
      }
      else {
        beanInstance = strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
      }

      bw.setBeanInstance(beanInstance);
      return bw;
    }
    catch (Throwable ex) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
          "Bean instantiation via constructor failed", ex);
    }
  }
註釋出處:https://blog.51cto.com/u_1424...

方法非常的長,我們總結一下可以歸為:

  1. 建構函式引數的確定根據傳入的explicitArgs引數判斷。如果 explicitArgs 不為空,則可以直接確定引數,因為 explicitArgs 是在呼叫 bean 的時候使用者指定的,在 BeanFactory 類中存在這樣的方法:Object getBean(String name,Object... args)。所以如果不為空,則直接可以確定建構函式引數就是它。除此之外,如果建構函式引數已經記錄在快取中,那麼便可以直接拿來使用。需要注意的是:在快取中拿到的可能是引數的最終型別也可能是初始型別,比如:引數要求是 int,而原始引數可能是 String,那麼即使在快取中得到引數,也需要進行型別轉換器的過濾以確保引數型別與對應的建構函式引數完全對應。

    如果以上兩種情況都無法確定。那麼只能進一步分析。通過 mbd.getConstructorArgumentValues()獲取配置的建構函式資訊,有了配置中的資訊便可以獲取對應的引數值資訊,獲取引數值的資訊包括指定值,也可能是對另一個 bean 的引用,這個處理委託給了 resolveConstructorArguments 方法,並返回能解析到的引數的個數。

  2. 建構函式的確定。我們確定了建構函式的引數後,就可以去根據引數去鎖定對應的建構函式。匹配的方法就是根據引數個數去進行匹配,在匹配前需要先對建構函式按照 public 優先引數數量降序,非 public 建構函式引數數量降序。

    由於還可以通過指定引數名稱設定引數值的情況,如<constructor-arg name="aa">,那麼這種情況首先要確定建構函式中的引數名稱,有兩種方式,一種通過註解的方式獲取,另一種通過 Spring 中的工具類 ParameterNameDiscoverer 來獲取。

  3. 根據確定的建構函式轉換對應的引數型別。主要是使用 Spring 中提供的型別轉換器或者使用者提供的自定義型別轉換器進行轉換。
  4. 建構函式不確定性的驗證。因為有時候即使建構函式、引數名稱、引數型別、引數值都確定也不一定就直接鎖定建構函式,不同建構函式的引數為父子關係,所以這裡最後又做一次驗證。
  5. 根據例項化策略以及得到的建構函式及建構函式引數例項化 bean

解析instanitateBean方法

我們瞭解了帶引數的方法後,我們接著看一下無參的預設構造器例項化過程。

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
   try {
      Object beanInstance;
      final BeanFactory parent = this;
      if (System.getSecurityManager() != null) {
         beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
                     getInstantiationStrategy().instantiate(mbd, beanName, parent),
               getAccessControlContext());
      } else {
         beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
      }
      BeanWrapper bw = new BeanWrapperImpl(beanInstance);
      initBeanWrapper(bw);
      return bw;
   } catch (Throwable ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
   }
}

其實裡面就是獲取例項化的策略,然後進行 instantiate 例項化。使用父類SimpleInstantiationStrategyinstantiate

  1. 例項化策略

我們提到了多次例項化策略,那這到底是什麼呢?其實經過前面的分析,我們已經得到了例項化的所有資訊,其實可以直接使用反射來進行例項化物件,但是 Spring 卻不是這樣做的。

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
   // Don't override the class with CGLIB if no overrides.
   //如果有需要覆蓋或者動態替換的方法需要使用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);
            }
         }
      }
      return BeanUtils.instantiateClass(constructorToUse);
   }
   else {
      // Must generate CGLIB subclass.
      return instantiateWithMethodInjection(bd, beanName, owner);
   }
}

CglibSubclassingInstantiationStrategy

public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {
   Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
   Object instance;
   if (ctor == null) {
      instance = BeanUtils.instantiateClass(subclass);
   }
   else {
      try {
         Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
         instance = enhancedSubclassConstructor.newInstance(args);
      }
      catch (Exception ex) {
         throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
               "Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
      }
   }
   // SPR-10785: set callbacks directly on the instance instead of in the
   // enhanced class (via the Enhancer) in order to avoid memory leaks.
   Factory factory = (Factory) instance;
   factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
         new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
         new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
   return instance;
}

首先在判斷了如果 beanDefinition.getMethodOverrides()為空則說明使用者使用 replace 或者 lookup 的配置方法,那就直接使用反射的方式。在使用了這兩種特性的話就只能使用動態代理。

記錄建立 bean 的 ObjectFactory

接下來我們繼續看 doCreateBean 方法,這裡進行了迴圈依賴檢查等

// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
//是否需要提前曝光,單例&&允許迴圈依賴&&當前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加入快取
   // 對bean 再一次依賴引用,主要應用SmartInstantiationAwareBeanPostProcessor
   // 其中我們熟悉的AOP就是在這裡將advice 動態織入,若沒有直接返回bean
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
  • earlySingletonExposure :提前曝光的例項
  • mbd.isSingleton():是否為單例
  • this.allowCircularReferences:是否允許迴圈依賴
  • isSingletonCurrentlyInCreation(beanName):該 bean 是否在建立中

這裡就拿 TestA 依賴 TestB,TestB 依賴 TestA 舉例。

迴圈依賴流程圖解

我們上一篇文章已經詳細講過。這裡就不再贅述。

相關文章