主頁 > 後端開發 > 死磕Spring之IoC篇 - Bean 的實體化階段

死磕Spring之IoC篇 - Bean 的實體化階段

2021-03-03 06:21:55 後端開發

該系列文章是本人在學習 Spring 的程序中總結下來的,里面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼注釋 Spring 原始碼分析 GitHub 地址 進行閱讀

Spring 版本:5.1.14.RELEASE

開始閱讀這一系列文章之前,建議先查看《深入了解 Spring IoC(面試題)》這一篇文章

該系列其他文章請查看:《死磕 Spring 之 IoC 篇 - 文章導讀》

Bean 的實體化階段

當我們顯示或者隱式地呼叫AbstractBeanFactorygetBean(...) 方法時,會觸發 Bean 的加載,在《開啟 Bean 的加載》文章中分析了整個加載程序,

對于不同作用域的 Bean,底層都會呼叫 AbstractAutowireCapableBeanFactorycreateBean(...) 方法進行創建,在上一篇《Bean 的創建程序》文章中分析了整個創建程序,在創建 Bean 的程序中,需要先獲取其 Class 物件,然后通過構造方法創建一個實體物件(反射機制),再進行后續的屬性填充和初始化作業,整個的實體化程序非常復雜,因為需要找到最匹配的構造方法,還需要找到該方法的入參,所以會有各種處理,本文將會分析創建 Bean 程序中的實體化階段,

回顧

先來回顧一下創建 Bean 程序中實體化階段對應的代碼:

// AbstractAutowireCapableBeanFactory#doCreateBean(...) 方法
// Instantiate the bean.
/**
 * <1> Bean 的實體化階段,會將 Bean 的實體物件封裝成 {@link BeanWrapperImpl} 包裝物件
 * BeanWrapperImpl 承擔的角色:
 * 1. Bean 實體的包裝
 * 2. {@link org.springframework.beans.PropertyAccessor} 屬性編輯器
 * 3. {@link org.springframework.beans.PropertyEditorRegistry} 屬性編輯器注冊表
 * 4. {@link org.springframework.core.convert.ConversionService} 型別轉換器(Spring 3+,替換了之前的 TypeConverter)
 */
BeanWrapper instanceWrapper = null;
// <1.1> 如果是單例模式,則先嘗試從 `factoryBeanInstanceCache` 快取中獲取實體物件,并從快取中移除
if (mbd.isSingleton()) {
    instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// <1.2> 使用合適的實體化策略來創建 Bean 的實體:工廠方法、建構式自動注入、簡單初始化
// 主要是將 BeanDefinition 轉換為 BeanWrapper 物件
if (instanceWrapper == null) {
    instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// <1.3> 獲取包裝的實體物件 `bean`
final Object bean = instanceWrapper.getWrappedInstance();
// <1.4> 獲取包裝的實體物件的型別 `beanType`
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
    mbd.resolvedTargetType = beanType;
}

Bean 的實體化階段,會將 Bean 的實體物件封裝成 BeanWrapperImpl 包裝物件,BeanWrapperImpl 承擔的角色

  • Bean 實體的包裝
  • PropertyAccessor 屬性編輯器
  • PropertyEditorRegistry 屬性編輯器注冊表
  • ConversionService 型別轉換器(Spring 3+,替換了之前的 TypeConverter)

如果是單例模式,則先嘗試從 factoryBeanInstanceCache 快取(保存 FactoryBean 型別的物件)中獲取實體物件,并從快取中移除,目前在創建 Bean 的程序中沒發現往這個集合中添加快取,暫時忽略,我們直接看到下面的一步,呼叫 createBeanInstance(...) 方法來創建一個實體物件,我們進去看看

開啟實體物件的創建

createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) 方法,創建一個 Bean 的實體物件,如下:

// AbstractAutowireCapableBeanFactory.java
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // Make sure bean class is actually resolved at this point.
    // <1> 獲取 `beanName` 對應的 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());
    }

    // <2> 如果存在 Supplier 實體化回呼介面,則使用給定的回呼方法創建一個實體物件
    Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
    if (instanceSupplier != null) {
        return obtainFromSupplier(instanceSupplier, beanName);
    }

    // <3> 如果配置了 `factory-method` 工廠方法,則呼叫該方法來創建一個實體物件
    // 通過 @Bean 標注的方法會通過這里進行創建
    if (mbd.getFactoryMethodName() != null) {
        // 這個程序非常復雜,你可以理解為:
        // 找到最匹配的 Method 工廠方法,獲取相關引數(依賴注入),然后通過呼叫該方法回傳一個實體物件(反射機制)
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }

    // Shortcut when re-creating the same bean...
    // <4> 判斷這個 RootBeanDefinition 的構造方法是否已經被決議出來了
    // 因為找到最匹配的構造方法比較繁瑣,找到后會設定到 RootBeanDefinition 中,避免重復這個程序
    boolean resolved = false;
    boolean autowireNecessary = false;
    if (args == null) {
        synchronized (mbd.constructorArgumentLock) { // 加鎖
            // <4.1> 構造方法已經決議出來了
            if (mbd.resolvedConstructorOrFactoryMethod != null) {
                resolved = true;
                // <4.2> 這個構造方法有入參,表示需要先獲取到對應的入參(構造器注入)
                autowireNecessary = mbd.constructorArgumentsResolved;
            }
        }
    }

    // <5> 如果最匹配的構造方法已決議出來
    if (resolved) {
        // <5.1> 如果這個構造方法有入參
        if (autowireNecessary) {
            // 這個程序很復雜,你可以理解為:
            // 找到最匹配的構造方法,這里會拿到已經被決議出來的這個方法,并找到入參(構造器注入),然后呼叫該方法回傳一個實體物件(反射機制)
            return autowireConstructor(beanName, mbd, null, null);
        }
        // <5.2> 否則,沒有入參
        else {
            // 直接呼叫決議出來構造方法,回傳一個實體物件(反射機制)
            return instantiateBean(beanName, mbd);
        }
    }

    // Candidate constructors for autowiring?
    // <6> 如果最匹配的構造方法還沒開始決議,那么需要找到一個最匹配的構造方法,然后創建一個實體物件

    /**
     * <6.1> 嘗試通過 SmartInstantiationAwareBeanPostProcessor 處理器的 determineCandidateConstructors 方法來找到一些合適的構造方法
     * 參考 {@link org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors}
     */
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    // <6.2> 是否滿足下面其中一個條件
    if (ctors != null // 上一步找到了合適的構造方法
            || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR // 構造器注入
            || mbd.hasConstructorArgumentValues() // 定義了構造方法的入參
            || !ObjectUtils.isEmpty(args)) // 當前方法指定了入參
    {
        // 找到最匹配的構造方法,如果 `ctors` 不為空,會從這里面找一個最匹配的,
        // 并找到入參(構造器注入),然后呼叫該方法回傳一個實體物件(反射機制)
        return autowireConstructor(beanName, mbd, ctors, args);
    }

    // Preferred constructors for default construction?
    /**
     * <7> 如果第 `6` 步還不滿足,那么嘗試獲取優先的構造方法
     * 參考 {@link org.springframework.context.support.GenericApplicationContext.ClassDerivedBeanDefinition}
     */
    ctors = mbd.getPreferredConstructors();
    if (ctors != null) {
        // <7.1> 如果存在優先的構造方法,則從里面找到最匹配的一個,并找到入參(構造器注入),然后呼叫該方法回傳一個實體物件(反射機制)
        return autowireConstructor(beanName, mbd, ctors, null);
    }

    // No special handling: simply use no-arg constructor.
    // <8> 如果上面多種情況都不滿足,那只能使用兜底方法了,直接呼叫默認構造方法回傳一個實體物件(反射機制)
    return instantiateBean(beanName, mbd);
}

程序大致如下:

  1. 獲取 beanName 對應的 Class 物件

  2. 如果存在 Supplier 實體化回呼介面,則使用給定的回呼方法創建一個實體物件,呼叫 obtainFromSupplier(...) 方法創建

  3. 如果配置了 factory-method 工廠方法,則呼叫該方法來創建一個實體物件,通過 @Bean 標注的方法會通過這里進行創建,呼叫 instantiateUsingFactoryMethod(...) 方法創建


如果上面兩種情況都不是,那么就進行接下來正常創建 Bean 實體的一個程序

  1. 判斷這個 RootBeanDefinition 的構造方法是否已經被決議出來了,因為找到最匹配的構造方法比較繁瑣,找到后會設定到 RootBeanDefinition 中,避免重復這個程序

    1. RootBeanDefinition 的 resolvedConstructorOrFactoryMethod 是否不為空,不為空表示構造方法已經決議出來了
    2. 構造方法已經決議出來了,則判斷它的 constructorArgumentsResolved 是否不為空,不為空表示有入參,需要先獲取到對應的入參(構造器注入)
  2. 如果最匹配的構造方法已決議出來

    1. 如果這個構造方法有入參,則找到最匹配的構造方法,這里會拿到已經被決議出來的這個方法,并找到入參(構造器注入),然后呼叫該方法回傳一個實體物件(反射機制),呼叫 autowireConstructor(...) 方法創建
    2. 否則,沒有入參,直接呼叫決議出來構造方法,回傳一個實體物件(反射機制),呼叫 instantiateBean(...) 方法創建
  3. 如果最匹配的構造方法還沒開始決議,那么需要找到一個最匹配的構造方法,然后創建一個實體物件

    1. 先嘗試通過 SmartInstantiationAwareBeanPostProcessor 處理器找到一些合適的構造方法,保存在 ctors

    2. 是否滿足下面其中一個條件:ctors 不為空、構造器注入模式、定義了構造方法的入參、當前方法指定了入參,

      則找到最匹配的構造方法,如果 ctors 不為空,會從這里面找一個最匹配的,并找到入參(構造器注入),然后呼叫該方法回傳一個實體物件(反射機制),呼叫 autowireConstructor(...) 方法創建

  4. 如果第 6 步還不滿足,那么嘗試從 RootBeanDefinition 中獲取優先的構造方法

    1. 如果存在優先的構造方法,則從里面找到最匹配的一個,并找到入參(構造器注入),然后呼叫該方法回傳一個實體物件(反射機制),呼叫 autowireConstructor(...) 方法創建
  5. 如果上面多種情況都不滿足,那只能使用兜底方法了,直接呼叫默認構造方法回傳一個實體物件(反射機制),呼叫 instantiateBean(...) 方法創建


整個的實體化程序非常的復雜,主要分為以下幾種情況:

  1. 指定了 Supplier 實體化回呼介面,則回呼該介面,回傳一個實體物件
  2. 配置了 factory-method 工廠方法創建當前 Bean,則找到這個方法,然后創建一個實體物件(@Bean 注解底層原理也是這種方式)
  3. 找到一個最匹配的構造方法,回傳一個實體物件,這個構造方法會設定到這個 RootBeanDefinition 中,避免再次決議,提高性能
  4. 兜底方法,使用默認構造方法回傳一個實體物件

這四種情況,其實就分別對應上面四個加粗的方法,接下來依次分析這四個方法

obtainFromSupplier 方法

obtainFromSupplier(Supplier<?> instanceSupplier, String beanName) 方法,通過 Supplier 回呼介面獲取一個實體物件,方法如下:

// AbstractAutowireCapableBeanFactory.java
protected BeanWrapper obtainFromSupplier(Supplier<?> instanceSupplier, String beanName) {
    Object instance;

    // 獲得原當前執行緒正在創建的 Bean 的名稱
    String outerBean = this.currentlyCreatedBean.get();
    // 設定當前執行緒正在創建的 Bean 的名稱
    this.currentlyCreatedBean.set(beanName);
    try {
        // <1> 呼叫 Supplier 的 get(),回傳一個實體物件
        instance = instanceSupplier.get();
    }
    finally {
        if (outerBean != null) {
            // 設定原當前執行緒正在創建的 Bean 的名稱到當前執行緒變數中
            this.currentlyCreatedBean.set(outerBean);
        }
        else {
            this.currentlyCreatedBean.remove();
        }
    }

    // 未創建 Bean 物件,則創建 NullBean 空物件
    if (instance == null) {
        instance = new NullBean();
    }
    // <2> 將實體物件封裝成 BeanWrapper 物件
    BeanWrapper bw = new BeanWrapperImpl(instance);
    // <3> 初始化這個 BeanWrapper 物件
    initBeanWrapper(bw);
    return bw;
}

程序如下:

  1. 呼叫 Supplier 介面的 get(),回傳 instance 實體物件
  2. instance 封裝成 BeanWrapper 物件 bw
  3. bw 進行初始化,設定 ConversionService 型別轉換器,并注冊自定義的屬性編輯器

整個程序比較簡單

instantiateUsingFactoryMethod 方法

通過 factoryMethodName 工廠方法創建一個實體物件,例如 XML 配置的 factory-method 屬性或者 @Bean 標注的方法都會決議成 factoryMethodName 屬性

這個程序非常復雜,你可以理解為去找到最匹配的 Method 工廠方法,獲取相關入參(依賴注入),然后呼叫該方法回傳一個實體物件(反射機制),方法如下:

// AbstractAutowireCapableBeanFactory.java
protected BeanWrapper instantiateUsingFactoryMethod(
        String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
    return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}

創建 ConstructorResolver 物件,然后呼叫其 instantiateUsingFactoryMethod(...) 方法,如下:

// ConstructorResolver.java
public BeanWrapper instantiateUsingFactoryMethod(
        String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {

    // 構造 BeanWrapperImpl 物件
    BeanWrapperImpl bw = new BeanWrapperImpl();
    // 初始化 BeanWrapperImpl,設定 ConversionService 型別轉換器,并注冊自定義的屬性編輯器
    this.beanFactory.initBeanWrapper(bw);

    // -------------------------獲取工廠方法的相關資訊-------------------------
    // <1> 獲取工廠方法的相關資訊

    // 工廠方法所在類對應的 Bean(靜態方法不會有)
    Object factoryBean;
    // 工廠方法所在類的 Class 物件
    Class<?> factoryClass;
    // 是否為 static 修飾的靜態方法
    boolean isStatic;

    // 獲取工廠方法所在類對應的 Bean 的名稱(靜態方法不會有)
    String factoryBeanName = mbd.getFactoryBeanName();
    // <1.1> 非靜態方法
    if (factoryBeanName != null) {
        if (factoryBeanName.equals(beanName)) {
            throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
                    "factory-bean reference points back to the same bean definition");
        }
        // 獲取工廠方法所在類對應的 Bean,不然無法呼叫工廠方法
        factoryBean = this.beanFactory.getBean(factoryBeanName);
        // 如果是單例模式,已經存在對應的 Bean,則拋出重復創建的例外
        if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
            throw new ImplicitlyAppearedSingletonException();
        }
        factoryClass = factoryBean.getClass();
        isStatic = false;
    }
    // <1.2> 靜態方法
    else {
        // It's a static factory method on the bean class.
        // 靜態方法沒有找到對應的 Class 物件無法被呼叫,則拋出例外
        if (!mbd.hasBeanClass()) {
            throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
                    "bean definition declares neither a bean class nor a factory-bean reference");
        }
        factoryBean = null;
        factoryClass = mbd.getBeanClass();
        isStatic = true;
    }

    // -------------------------嘗試獲取工廠方法物件和入參-------------------------
    // <2> 嘗試獲取工廠方法物件和引數

    // 工廠方法物件
    Method factoryMethodToUse = null;
    ArgumentsHolder argsHolderToUse = null;
    // 方法引數
    Object[] argsToUse = null;

    // <2.1> 如果方法入參指定了引數,則直接使用
    if (explicitArgs != null) {
        argsToUse = explicitArgs;
    }
    // <2.2> 否則,嘗試從 RootBeanDefinition 中獲取已決議出來的工廠方法和入參
    else {
        Object[] argsToResolve = null;
        // 因為可能前面決議了,會臨時快取,避免再次決議
        synchronized (mbd.constructorArgumentLock) { // 加鎖
            factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
            // 如果工廠方法被決議了,那么引數也可能決議過
            if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
                // Found a cached factory method...
                argsToUse = mbd.resolvedConstructorArguments;
                if (argsToUse == null) {
                    // 沒有決議過的引數,則嘗試從 RootBeanDefinition 中獲取未被決議過的引數
                    argsToResolve = mbd.preparedConstructorArguments;
                }
            }
        }
        // 如果獲取到了未被決議過的入參,則進行決議
        if (argsToResolve != null) {
            // 處理引數值,型別轉換,例如給定方法 A(int, int),配置了 A("1"、"2") 兩個引數,則會轉換為 A(1, 1)
            argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);
        }
    }

    // -------------------------找到所有匹配的工廠方法-------------------------
    // <3> 如果上一步沒有找到工廠方法物件或方法入參集合,則需要進行接下來的決議程序,首先找到所有匹配的工廠方法

    if (factoryMethodToUse == null || argsToUse == null) {
        // Need to determine the factory method...
        // Try all methods with this name to see if they match the given arguments.
        // <3.1> 獲取工廠方法所在的類的實體 Class 物件,因為可能是 Cglib 提升過的子類
        factoryClass = ClassUtils.getUserClass(factoryClass);

        // <3.2> 獲取工廠方法所在的類中所有方法物件
        Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
        // <3.3> 找到這個類中匹配的工廠方法
        List<Method> candidateList = new ArrayList<>();
        for (Method candidate : rawCandidates) {
            if (Modifier.isStatic(candidate.getModifiers()) == isStatic // 是否和 `isStatic` 匹配
                    && mbd.isFactoryMethod(candidate)) { // 和定義的工廠方法的名稱是否相等
                candidateList.add(candidate);
            }
        }

        // <3.4> 如果只有一個匹配的方法,且這個方法沒有給指定的入參,且本身也沒有定義引數,且這個方法沒有定義入參
        // 則直接呼叫這個方法創建一個實體物件(反射機制),并回傳
        if (candidateList.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
            Method uniqueCandidate = candidateList.get(0);
            if (uniqueCandidate.getParameterCount() == 0) {
                mbd.factoryMethodToIntrospect = uniqueCandidate;
                synchronized (mbd.constructorArgumentLock) {
                    mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
                    mbd.constructorArgumentsResolved = true;
                    mbd.resolvedConstructorArguments = EMPTY_ARGS;
                }
                bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
                return bw;
            }
        }

        // -------------------------開始找最匹配的工廠方法-------------------------
        // <4> 開始找最匹配的工廠方法

        // 將匹配的工廠方法轉換成陣列
        Method[] candidates = candidateList.toArray(new Method[0]);
        // 將匹配的方法進行排序,public 方法優先,入參個數多的優先
        AutowireUtils.sortFactoryMethods(candidates);

        // 用于承載決議后的方法引數值
        ConstructorArgumentValues resolvedValues = null;
        // 是否是構造器注入
        boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
        int minTypeDiffWeight = Integer.MAX_VALUE;
        // 匹配方法的集合
        Set<Method> ambiguousFactoryMethods = null;

        // -------------------------確定方法引數的入引數量-------------------------
        // <5> 確定方法引數的入引數量,匹配的方法的入引數量要多余它
        // 方法的引數數量的最小值
        int minNrOfArgs;
        // <5.1> 如果當前方法指定了入參,則使用其個數作為最小值
        if (explicitArgs != null) {
            minNrOfArgs = explicitArgs.length;
        }
        // <5.1> 否則,從 RootBeanDefinition 決議出方法的引數個數作為最小值
        else {
            // We don't have arguments passed in programmatically, so we need to resolve the
            // arguments specified in the constructor arguments held in the bean definition.
            // RootBeanDefinition 定義了引數值
            if (mbd.hasConstructorArgumentValues()) {
                // 方法的引數
                ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
                resolvedValues = new ConstructorArgumentValues();
                // 決議定義的引數值,放入 `resolvedValues` 中,并回傳引數個數
                minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
            }
            else {
                minNrOfArgs = 0;
            }
        }

        // 記錄 UnsatisfiedDependencyException 例外的集合
        LinkedList<UnsatisfiedDependencyException> causes = null;

        // 遍歷匹配的方法
        for (Method candidate : candidates) {
            // 方法體的引數
            Class<?>[] paramTypes = candidate.getParameterTypes();

            if (paramTypes.length >= minNrOfArgs) {
                // -------------------------決議出工廠方法的入參-------------------------
                // <6> 決議出工廠方法的入參

                // 保存引數的物件
                ArgumentsHolder argsHolder;

                // <6.1> 如果當前方法指定了入參,則直接使用
                if (explicitArgs != null) {
                    // Explicit arguments given -> arguments length must match exactly.
                    // 顯示給定引數,引數長度必須完全匹配
                    if (paramTypes.length != explicitArgs.length) {
                        continue;
                    }
                    // 根據引數創建引數持有者 ArgumentsHolder 物件
                    argsHolder = new ArgumentsHolder(explicitArgs);
                }
                // <6.2> 否則,通過**依賴注入**獲取入參
                else {
                    // Resolved constructor arguments: type conversion and/or autowiring necessary.
                    // 為提供引數,決議構造引數
                    try {
                        String[] paramNames = null;
                        // 獲取 ParameterNameDiscoverer 引數名稱探測器
                        ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
                        // 獲取方法的引數名稱
                        if (pnd != null) {
                            paramNames = pnd.getParameterNames(candidate);
                        }
                        // 決議出方法的入參,引數值會被依賴注入
                        argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
                                paramTypes, paramNames, candidate, autowiring, candidates.length == 1);
                    }
                    catch (UnsatisfiedDependencyException ex) {
                        // 若發生 UnsatisfiedDependencyException 例外,添加到 causes 中,
                        if (logger.isTraceEnabled()) {
                            logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex);
                        }
                        // Swallow and try next overloaded factory method.
                        if (causes == null) {
                            causes = new LinkedList<>();
                        }
                        causes.add(ex);
                        continue;
                    }
                }

                // -------------------------根據權重獲取最匹配的方法-------------------------
                // <7> 因為會遍歷所有匹配的方法,所以需要進行權重的判斷,拿到最優先的那個

                // 判斷決議建構式的時候是否以寬松模式還是嚴格模式,默認為 true
                // 嚴格模式:決議建構式時,必須所有的都需要匹配,否則拋出例外
                // 寬松模式:使用具有"最接近的模式"進行匹配
                // typeDiffWeight:型別差異權重
                int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
                        argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
                // Choose this factory method if it represents the closest match.
                // 代表最匹配的結果,則選擇作為符合條件的方法
                if (typeDiffWeight < minTypeDiffWeight) {
                    factoryMethodToUse = candidate;
                    argsHolderToUse = argsHolder;
                    argsToUse = argsHolder.arguments;
                    minTypeDiffWeight = typeDiffWeight;
                    ambiguousFactoryMethods = null;
                }
                // Find out about ambiguity: In case of the same type difference weight
                // for methods with the same number of parameters, collect such candidates
                // and eventually raise an ambiguity exception.
                // However, only perform that check in non-lenient constructor resolution mode,
                // and explicitly ignore overridden methods (with the same parameter signature).
                // 如果具有相同引數數量的方法具有相同的型別差異權重,則收集此型別選項
                // 但是,僅在非寬松建構式決議模式下執行該檢查,并顯式忽略重寫方法(具有相同的引數簽名)
                else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&
                        !mbd.isLenientConstructorResolution() &&
                        paramTypes.length == factoryMethodToUse.getParameterCount() &&
                        !Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {
                    // 查找到多個可匹配的方法
                    if (ambiguousFactoryMethods == null) {
                        ambiguousFactoryMethods = new LinkedHashSet<>();
                        ambiguousFactoryMethods.add(factoryMethodToUse);
                    }
                    ambiguousFactoryMethods.add(candidate);
                }
            }
        }

        // <8> 沒有找到對應的工廠方法,則拋出例外
        if (factoryMethodToUse == null) {
            if (causes != null) {
                UnsatisfiedDependencyException ex = causes.removeLast();
                for (Exception cause : causes) {
                    this.beanFactory.onSuppressedException(cause);
                }
                throw ex;
            }
            List<String> argTypes = new ArrayList<>(minNrOfArgs);
            if (explicitArgs != null) {
                for (Object arg : explicitArgs) {
                    argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null");
                }
            }
            else if (resolvedValues != null) {
                Set<ValueHolder> valueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount());
                valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values());
                valueHolders.addAll(resolvedValues.getGenericArgumentValues());
                for (ValueHolder value : valueHolders) {
                    String argType = (value.getType() != null ? ClassUtils.getShortName(value.getType()) :
                            (value.getValue() != null ? value.getValue().getClass().getSimpleName() : "null"));
                    argTypes.add(argType);
                }
            }
            String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "No matching factory method found: " +
                    (mbd.getFactoryBeanName() != null ?
                        "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
                    "factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +
                    "Check that a method with the specified name " +
                    (minNrOfArgs > 0 ? "and arguments " : "") +
                    "exists and that it is " +
                    (isStatic ? "static" : "non-static") + ".");
        }
        else if (void.class == factoryMethodToUse.getReturnType()) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Invalid factory method '" + mbd.getFactoryMethodName() +
                    "': needs to have a non-void return type!");
        }
        else if (ambiguousFactoryMethods != null) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Ambiguous factory method matches found in bean '" + beanName + "' " +
                    "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
                    ambiguousFactoryMethods);
        }

        // <9> 將決議出來的工廠方法和入參快取,設定到 RootBeanDefinition 中,因為整個程序比較復雜,避免再次決議
        if (explicitArgs == null && argsHolderToUse != null) {
            mbd.factoryMethodToIntrospect = factoryMethodToUse;
            argsHolderToUse.storeCache(mbd, factoryMethodToUse);
        }
    }

    Assert.state(argsToUse != null, "Unresolved factory method arguments");
     // <10> 呼叫工廠方法創建一個實體物件(反射機制),并設定到 `bw` 中
    bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));
    return bw;
}

這個方法我真的是吐了??,也太長了吧,硬著頭皮看完了一部分,參考檔案:ConstructorResolver.java

對于程序不感興趣的可以跳過直接看下面的概括,程序大致如下:

先創建一個 BeanWrapperImpl 物件 bw,并初始化,設定 ConversionService 型別轉換器,并注冊自定義的屬性編輯器


  1. 獲取工廠方法的相關資訊,根據 RootBeanDefinition 的 factoryBeanName 屬性判斷是否靜態方法,這個屬性表示這個工廠方法所在類對應 Bean 的名稱,當然靜態方法“不屬于”這個 Bean,所以它的這個屬性為空
    1. factoryBeanName 屬性不為空,表示不是靜態方法,則需要根據 factoryBeanName 找到(依賴查找)對應的 Bean,作為 factoryBean
    2. 否則,就是靜態方法,獲取所在類的 Class 物件即可

這一步找到了三個物件:factoryBean(工廠方法所在類對應的 Bean,靜態方法不會有)、factoryClass(工廠方法所在類的 Class 物件)、isStatic(是否為靜態方法),所以想要通過工廠方法獲取一個 Bean,則需要方法所在類對應的 Bean 先初始化,然后才能呼叫這個方法創建 Bean;而靜態方法就不用,因為它可以根據所在類的 Class 物件就能呼叫這個方法創建 Bean,這就是兩者的區別,


  1. 嘗試獲取工廠方法 Method 物件和入參

    1. 如果方法入參指定了引數,則直接使用

    2. 否則,嘗試從 RootBeanDefinition 中獲取已決議出來的工廠方法和入參,如果獲取到了未被決議過的入參,則進行決議(型別轉換)

      例如給定方法 A(int, int),配置了 A("1"、"2") 兩個引數,則會轉換為 A(1, 1)

這一步嘗試獲取兩個物件:factoryMethodToUse(對應的工廠方法 Method)、argsToUse(工廠方法的入參集合)


  1. 如果上一步沒有找到工廠方法物件或方法入參集合,找到所有匹配的工廠方法,首先找到所有匹配的工廠方法
    1. 獲取工廠方法所在的類的實體 Class 物件,因為可能是 Cglib 提升過的子類
    2. 獲取工廠方法所在的類中所有方法物件
    3. 找到這個類中匹配的工廠方法,是否和 isStatic 匹配,并且和定義的工廠方法的名稱是否相等
    4. 如果只有一個匹配的方法,且這個方法沒有給指定的入參,且本身也沒有定義引數,且這個方法沒有定義入參,則直接呼叫這個方法創建一個實體物件(反射機制),并回傳

上面第 3.3 步找到的通常只有一個,如果沒有入參則可以直接進入第 3.4 步,使用這個方法創建一個實體物件,不過你可能定義了多載方法,也可能定義了方法引數,所以需要進行接下來的決議程序


  1. 開始找最匹配的工廠方法,先排序,public 方法優先,入參個數多的優先
  2. 確定方法引數的入引數量,匹配的方法的入引數量要多余它
    1. 如果當前方法指定了入參,則使用其個數作為最小值
    2. 否則,從 RootBeanDefinition 決議出方法的引數個數作為最小值,沒有定義則設定為 0

這一步主要是確定入參的個數,并排序所有匹配的方法,接下來會遍歷所有匹配的方法


  1. 決議出工廠方法的入參
    1. 如果當前方法指定了入參,則直接使用
    2. 否則,通過依賴注入獲取入參

這一步會找到這個方法的入參,依賴注入的方式


  1. 因為會遍歷所有匹配的方法,所以需要進行權重的判斷,拿到最優先的那個

通常情況下,我們只有一個匹配的方法,如果存在多個,會根據方法的引數型別進行權重


  1. 沒有找到對應的工廠方法,則拋出例外
  2. 將決議出來的工廠方法和入參快取,設定到 RootBeanDefinition 中,因為整個程序比較復雜,避免再次決議

會快取這幾個資料:resolvedConstructorOrFactoryMethod(已經決議出來的工廠方法)、constructorArgumentsResolved(方法入參已經決議出來了 true)、resolvedConstructorArguments(決議出來的入參)


  1. 呼叫工廠方法創建一個實體物件(反射機制),并設定到 bw

上面整個程序非常復雜,這里進行簡單概括

  • 找到對應的工廠方法,如果是非靜態方法,則需要先依賴查找到所在類對應的 Bean,因為需要根據這個 Bean 去呼叫對應的工廠方法,而靜態方法不用,可以根據其 Class 物件呼叫對應的工廠方法

  • 如果工廠方法有入參,則需要注入相關物件(依賴注入

  • 呼叫這個方法(反射機制),回傳一個實體物件

autowireConstructor 方法

這個程序和上一個方法一樣非常復雜,不過差不太多,你可以理解為去找到當前 Bean 的構造方法,獲取相關入參(構造器注入),然后呼叫該構造方法回傳一個實體物件(反射機制),方法如下:

// AbstractAutowireCapableBeanFactory.java
protected BeanWrapper autowireConstructor(
        String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
    return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}

創建 ConstructorResolver 物件,然后呼叫其 autowireConstructor(...) 方法,如下:

// ConstructorResolver.java
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
        @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {

    // 構造 BeanWrapperImpl 物件
    BeanWrapperImpl bw = new BeanWrapperImpl();
    // 初始化 BeanWrapperImpl,設定 ConversionService 型別轉換器,并注冊自定義的屬性編輯器
    this.beanFactory.initBeanWrapper(bw);

    // -------------------------嘗試獲取構造方法和入參-------------------------
    // <1> 嘗試獲取構造方法和入參

    // 構造方法
    Constructor<?> constructorToUse = null;
    ArgumentsHolder argsHolderToUse = null;
    // 構造方法的入參集合
    Object[] argsToUse = null;

    // <1.1> 如果當前方法入參指定了引數,則直接使用
    if (explicitArgs != null) {
        argsToUse = explicitArgs;
    }
    // <1.2> 否則,嘗試從 RootBeanDefinition 中獲取已決議出來的構造方法和入參
    else {
        // 因為可能前面決議了,會臨時快取,避免再次決議
        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) {
                    // 沒有決議過的引數,則嘗試從 RootBeanDefinition(合并后)中獲取未被決議過的引數
                    argsToResolve = mbd.preparedConstructorArguments;
                }
            }
        }
        // 如果獲取到了未被決議過的入參
        if (argsToResolve != null) {
            // 處理引數值,型別轉換,例如給定方法 A(int, int),配配置了 A("1"、"2") 兩個引數,則會轉換為 A(1, 1)
            argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
        }
    }

    // -------------------------開始獲取構造方法和入參-------------------------
    // <2> 如果上一步沒有找到構造方法或入參集合,找到所有匹配的工廠方法,首先找到所有匹配的構造方法

    if (constructorToUse == null || argsToUse == null) {
        // Take specified constructors, if any.
        // <2.1> 獲取所有的構造方法,如果當前方法指定了構造方法的集合,則使用這個集合
        Constructor<?>[] candidates = chosenCtors;
        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);
            }
        }

        // <2.2> 如果構造方法只有一個,且當前方法的入參沒有指定引數,且本身也沒有定義引數,且這個構造方法沒有定義入參
        // 則直接呼叫這個構造方法創建一個實體物件(反射機制),并回傳
        if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
            Constructor<?> uniqueCandidate = candidates[0];
            if (uniqueCandidate.getParameterCount() == 0) {
                synchronized (mbd.constructorArgumentLock) {
                    mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
                    mbd.constructorArgumentsResolved = true;
                    mbd.resolvedConstructorArguments = EMPTY_ARGS;
                }
                bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
                return bw;
            }
        }

        // Need to resolve the constructor.
        // 是否是構造器注入
        boolean autowiring = (chosenCtors != null ||
                mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
        // 用于承載決議后的方法引數值
        ConstructorArgumentValues resolvedValues = null;

        // -------------------------確定構造方法的入引數量-------------------------
        // <3> 確定構造引數的入引數量,匹配的方法的入引數量要多余它
        // 方法的引數數量的最小值
        int minNrOfArgs;
        if (explicitArgs != null) {
            minNrOfArgs = explicitArgs.length;
        }
        // 從 RootBeanDefinition 決議出方法的引數個數作為最小值
        else {
            ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
            resolvedValues = new ConstructorArgumentValues();
            minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
        }

        // <4> 將所有的構造方法進行排序,public 方法優先,入參個數多的優先
        AutowireUtils.sortConstructors(candidates);
        int minTypeDiffWeight = Integer.MAX_VALUE;
        Set<Constructor<?>> ambiguousConstructors = null;
        LinkedList<UnsatisfiedDependencyException> causes = null;

        // 遍歷所有建構式
        for (Constructor<?> candidate : candidates) {
            // 獲取該構造方法的引數型別
            Class<?>[] paramTypes = candidate.getParameterTypes();

            // 如果前面已經找到匹配的構造方法和入參,則直接結束回圈
            if (constructorToUse != null && argsToUse != 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;
            }
            // 如果這個構造方法的引數個數小于入引數量,則跳過
            if (paramTypes.length < minNrOfArgs) {
                continue;
            }

            // -------------------------決議出構造方法的入參-------------------------
            // <5> 決議出構造方法的入參

            // 保存引數的物件
            ArgumentsHolder argsHolder;
            // <5.2> 通過**依賴注入**獲取入參
            if (resolvedValues != null) {
                try {
                    // 獲取構造方法的引數名稱(@ConstructorProperties 注解)
                    String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
                    if (paramNames == null) {
                        // 沒有獲取到則通過 ParameterNameDiscoverer 引數探測器獲取引數名稱
                        ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
                        if (pnd != null) {
                            paramNames = pnd.getParameterNames(candidate);
                        }
                    }
                    // 決議出方法的入參,引數值會被依賴注入
                    argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
                            getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
                }
                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;
                }
            }
            // <5.1> 如果當前方法的入參指定了引數,如果個數相等則直接使用
            else {
                // Explicit arguments given -> arguments length must match exactly.
                if (paramTypes.length != explicitArgs.length) {
                    continue;
                }
                argsHolder = new ArgumentsHolder(explicitArgs);
            }

            // -------------------------根據權重獲取最匹配的方法-------------------------
            // <6> 因為會遍歷所有匹配的方法,所以需要進行權重的判斷,拿到最優先的那個

            // 判斷決議建構式的時候是否以寬松模式還是嚴格模式,默認為 true
            // 嚴格模式:決議建構式時,必須所有的都需要匹配,否則拋出例外
            // 寬松模式:使用具有"最接近的模式"進行匹配
            // typeDiffWeight:型別差異權重
            int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
                    argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
            // Choose this constructor if it represents the closest match.
            // 代表最匹配的結果,則選擇作為符合條件的方法
            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);
            }
        }

        // <7> 沒有找到對應的構造方法,則拋出例外
        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)");
        }
        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);
        }

        // <8> 將決議出來的工廠方法和入參快取,設定到 RootBeanDefinition 中,因為整個程序比較復雜,避免再次決議
        if (explicitArgs == null && argsHolderToUse != null) {
            argsHolderToUse.storeCache(mbd, constructorToUse);
        }
    }

    Assert.state(argsToUse != null, "Unresolved constructor arguments");
    // <9> 呼叫這個構造方法回傳一個實體物件(反射機制),并設定到 `bw` 中
    bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
    return bw;
}

這個方法我也是吐了??,不過還好,和上一個方法邏輯差不多,硬著頭皮,參考檔案:ConstructorResolver.java

對于程序不感興趣的可以跳過直接看下面的概括,程序大致如下:

先創建一個 BeanWrapperImpl 物件 bw,并初始化,設定 ConversionService 型別轉換器,并注冊自定義的屬性編輯器


  1. 嘗試獲取構造方法和入參

    1. 如果當前方法入參指定了引數,則直接使用

    2. 否則,嘗試從 RootBeanDefinition 中獲取已決議出來的構造方法和入參,如果獲取到了未被決議過的入參,則進行決議(型別轉換)

      例如給定方法 A(int, int),配置了 A("1"、"2") 兩個引數,則會轉換為 A(1, 1)

這一步嘗試獲取兩個物件:constructorToUse(構造方法)、argsToUse(構造方法的入參集合)


  1. 如果上一步沒有找到構造方法或入參集合,找到所有匹配的工廠方法,首先找到所有匹配的構造方法

    1. 獲取所有的構造方法,如果當前方法指定了構造方法的集合,則使用這個集合

    2. 如果構造方法只有一個,且當前方法的入參沒有指定引數,且本身也沒有定義引數,且這個構造方法沒有定義入參

      則直接呼叫這個構造方法創建一個實體物件(反射機制),并回傳

上面第 2.2 步,通常只有默認構造方法才會直接呼叫并回傳的,否則需要進行接下來的決議程序


  1. 確定構造引數的入引數量,匹配的方法的入引數量要多余它
  2. 將所有的構造方法進行排序,public 方法優先,入參個數多的優先

這一步主要是確定入參的個數,并排序所有匹配的構造方法,接下來會遍歷所有匹配的構造方法


如果前面已經找到匹配的構造方法和入參,則直接結束回圈;如果這個構造方法的引數個數小于入引數量,則跳過

  1. 決議出構造方法的入參
    1. 如果當前方法的入參指定了引數,如果個數相等則直接使用
    2. 否則,通過構造器注入獲取入參

這一步會找到這個構造方法的入參,構造器注入的方式


  1. 因為會遍歷所有匹配的方法,所以需要進行權重的判斷,拿到最優先的那個,會根據方法的引數型別進行權重

  1. 沒有找到對應的構造方法,則拋出例外
  2. 將決議出來的工廠方法和入參快取,設定到 RootBeanDefinition 中,因為整個程序比較復雜,避免再次決議

會快取這幾個資料:resolvedConstructorOrFactoryMethod(已經決議出來的構造方法)、constructorArgumentsResolved(方法入參已經決議出來了 true)、resolvedConstructorArguments(決議出來的入參)


  1. 呼叫這個構造方法回傳一個實體物件(反射機制),并設定到 bw

上面整個程序非常復雜,這里進行簡單概括

  • 找到最匹配的構造方法

  • 如果構造方法有入參,則需要注入相關物件(構造器注入,其實也是依賴注入獲取到的引數)

  • 呼叫這個構造方法(反射機制),回傳一個實體物件

instantiateBean 方法

兜底方法,如果構造方法找不到(或者已經決議出來的構造方法),則直接使用默認的構造方法(或者已經決議出來的構造方法),回傳一個實體物件,方法如下:

// AbstractAutowireCapableBeanFactory.java
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>) () ->
                    // 獲得 InstantiationStrategy 物件,并使用它,創建 Bean 物件
                    getInstantiationStrategy().instantiate(mbd, beanName, parent),
                    getAccessControlContext());
        }
        else {
             // 獲得 InstantiationStrategy 物件,并使用它,創建 Bean 物件
            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);
    }
}

可以看到會通過 CglibSubclassingInstantiationStrategy#instantiate(...) 方法創建一個實體物件,該方法如下:

// SimpleInstantiationStrategy.java
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    // Don't override the class with CGLIB if no overrides.
    // <1> 沒有 MethodOverride 物件,也就是沒有需要覆寫或替換的方法,則直接使用反射機制進行實體化即可
    if (!bd.hasMethodOverrides()) {
        Constructor<?> constructorToUse;
        synchronized (bd.constructorArgumentLock) {
            // <1.1> 嘗試從 RootBeanDefinition 獲得已經決議出來的構造方法 `resolvedConstructorOrFactoryMethod`
            constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
            // <1.2> 沒有決議出來的構造方法,則獲取默認的構造方法
            if (constructorToUse == null) {
                final Class<?> clazz = bd.getBeanClass();
                // 如果是介面,拋出 BeanInstantiationException 例外
                if (clazz.isInterface()) {
                    throw new BeanInstantiationException(clazz, "Specified class is an interface");
                }
                try {
                    // 從 clazz 中,獲得構造方法
                    if (System.getSecurityManager() != null) { // 安全模式
                        constructorToUse = AccessController.doPrivileged(
                                (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
                    }
                    else {
                        constructorToUse = clazz.getDeclaredConstructor();
                    }
                    // 標記 resolvedConstructorOrFactoryMethod 屬性
                    bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                }
                catch (Throwable ex) {
                    throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                }
            }
        }
        // <1.3> 通過這個構造方法實體化一個物件(反射機制)
        return BeanUtils.instantiateClass(constructorToUse);
    }
    // <2> 否則,通過 CGLIB 生成一個子類物件
    else {
        // Must generate CGLIB subclass.
        return instantiateWithMethodInjection(bd, beanName, owner);
    }
}

程序大致如下:

  1. 沒有 MethodOverride 物件,也就是沒有需要覆寫或替換的方法,則直接使用反射機制進行實體化即可
    1. 嘗試從 RootBeanDefinition 獲得已經決議出來的構造方法 resolvedConstructorOrFactoryMethod
    2. 沒有決議出來的構造方法,則獲取默認的構造方法
    3. 通過這個構造方法實體化一個物件(反射機制)
  2. 否則,通過 CGLIB 生成一個子類物件,該程序暫時忽略

整個程序并不復雜

總結

當我們顯示或者隱式地呼叫AbstractBeanFactorygetBean(...) 方法時,會觸發 Bean 的加載,在《開啟 Bean 的加載》文章中分析了整個加載程序,

對于不同作用域的 Bean,底層都會呼叫 AbstractAutowireCapableBeanFactorycreateBean(...) 方法進行創建,在上一篇《Bean 的創建程序》文章中分析了整個創建程序,在創建 Bean 的程序中,需要先創建一個實體物件,這個程序在實體化階段完成,主要分為下面幾種情況:

  1. 指定了 Supplier 實體化回呼介面,則回呼該介面,回傳一個實體物件
  2. 配置了 factory-method 工廠方法創建當前 Bean,則找到這個方法,如果有入參,則需要找到對應的引數(依賴注入),然后創建一個實體物件(@Bean 注解底層原理也是這種方式)
  3. 找到一個最匹配的構造方法,如果有入參則需要找到對應的引數(構造器注入),回傳一個實體物件,這個構造方法會設定到這個 RootBeanDefinition 中,避免再次決議,提高性能
  4. 兜底方法,使用默認構造方法回傳一個實體物件

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/265290.html

標籤:其他

上一篇:物件、類

下一篇:零基礎學Python:串列推導式及深淺拷貝

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more