Spring IOC源碼解析-創建原始bean對象

一.簡介

這篇文章是之前一篇文章(創建單例bean的過程)的延續。在上篇文章中,從大體上講述了doCreateBean方法的過程。今天,詳細講解下這個方法過程中的一個重要調用,即createBeanInstance方法。

二.源碼分析

2.1 創建bean對象的過程

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
    Class<?> beanClass = resolveBeanClass(mbd, beanName);

    /*
     * 檢測類的訪問權限。默認情況下,對於非 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());
    }

    /*
     * 如果工廠方法不爲空,則通過工廠方法構建 
     */
    if (mbd.getFactoryMethodName() != null)  {
        // 通過“工廠方法”的方式構建 bean 對象
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }

    /*
     * 當多次構建同一個 bean 時,可以使用此處的快捷路徑,即無需再次推斷應該使用哪種方式構造實例,
     * 以提高效率。比如在多次構建同一個 prototype 類型的 bean 時,就可以走此處的捷徑。
     * 這裏的 resolved 和 mbd.constructorArgumentsResolved 將會在 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) {
            // 通過“構造方法自動注入”的方式構造 bean 對象
            return autowireConstructor(beanName, mbd, null, null);
        }
        else {
            // 通過“默認構造方法”的方式構造 bean 對象
            return instantiateBean(beanName, mbd);
        }
    }

    // 由後置處理器決定返回哪些構造方法
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    /*
     * 下面的條件分支條件用於判斷使用什麼方式構造 bean 實例,有兩種方式可選 - 構造方法自動
     * 注入和默認構造方法。判斷的條件由4部分綜合而成,如下:
     * 
     *    條件1:ctors != null -> 後置處理器返回構造方法數組是否爲空
     *    
     *    條件2:mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR 
     *              -> bean 配置中的 autowire 屬性是否爲 constructor    
     *    條件3:mbd.hasConstructorArgumentValues() 
     *              -> constructorArgumentValues 是否存在元素,即 bean 配置文件中
     *                 是否配置了 <construct-arg/>
     *    條件4:!ObjectUtils.isEmpty(args) 
     *              -> args 數組是否存在元素,args 是由用戶調用 
     *                 getBean(String name, Object... args) 傳入的
     * 
     * 上面4個條件,只要有一個爲 true,就會通過構造方法自動注入的方式構造 bean 實例
     */
    if (ctors != null ||
            mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
            mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
        // 通過“構造方法自動注入”的方式構造 bean 對象
        return autowireConstructor(beanName, mbd, ctors, args);
    }

    // 通過“默認構造方法”的方式構造 bean 對象
    return instantiateBean(beanName, mbd);
}

這個方法的執行流程:

  1. 檢測類的訪問權限,若禁止訪問,則拋出異常
  2. 如果在RootBeanDefinition中存在factoryMethodName屬性,或者說在配置文件中配置了factory-method方法,那麼對嘗試使用instantiateUsingFactoryMethod(beanName, mbd, args)方法根據RootBeanDefinition中的配置生成bean的實例。
  3. 若構造方式已解析過,則走快捷路徑構建 bean 對象,並返回結果。
  4. 如第三步不滿足,則通過組合條件決定使用哪種方式構建 bean 對象。

在這個方法中體現了三種構造bean對象的方式,如下:

  1. 通過“工廠方法”的方式構造bean對象

  2. 通過“構造方法自動注入”的方式構造bean對象

  3. 通過“默認構造方法”的方式構造bean對象

接下來會詳細介紹下這幾種方式。

2.2 通過構造方法自動注入的方式創建bean實例

protected BeanWrapper autowireConstructor(
        String beanName, RootBeanDefinition mbd, Constructor<?>[] ctors, Object[] explicitArgs) {

    // 創建 ConstructorResolver 對象,並調用其 autowireConstructor 方法
    return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}

public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd,
        Constructor<?>[] chosenCtors, final Object[] explicitArgs) {

    // 創建 BeanWrapperImpl 對象
    BeanWrapperImpl bw = new BeanWrapperImpl();
    this.beanFactory.initBeanWrapper(bw);

    Constructor<?> constructorToUse = null;
    ArgumentsHolder argsHolderToUse = null;
    Object[] argsToUse = null;

    // 確定參數值列表(argsToUse)
    if (explicitArgs != null) {
        argsToUse = explicitArgs;
    }
    else {
        Object[] argsToResolve = null;
        synchronized (mbd.constructorArgumentLock) {
            // 獲取已解析的構造方法
            constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
            if (constructorToUse != null && mbd.constructorArgumentsResolved) {
                // 獲取已解析的構造方法參數列表
                argsToUse = mbd.resolvedConstructorArguments;
                if (argsToUse == null) {
                    // 若 argsToUse 爲空,則獲取未解析的構造方法參數列表
                    argsToResolve = mbd.preparedConstructorArguments;
                }
            }
        }
        if (argsToResolve != null) {
            // 解析參數類型
            argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
        }
    }
  
 //上面總結爲:構造參數不爲空,直接使用;如果緩存中有已經解析的構造函數或者工廠方法,可以從緩存中獲得構造參數參數。這個爲空的話,從緩存中獲得構造函數,然後通過構造函數獲得構造參數類型。

   //如果緩存中構造器不存在,說明bean沒有進行過解析
    if (constructorToUse == null) {
        //獲得bean的構造器
        //如果傳入的構造器不爲空,獲得注入方式
        boolean autowiring = (chosenCtors != null ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
        ConstructorArgumentValues resolvedValues = null;

        int minNrOfArgs;
        if (explicitArgs != null) {
            minNrOfArgs = explicitArgs.length;
        }
        else {
             //從RootBeanDefinition中獲取構造參數,並解析參數,添加到cargs
            ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
            resolvedValues = new ConstructorArgumentValues();
            /*
             * 確定構造方法參數數量,比如下面的配置:
             *     <bean id="persion" class="xyz.coolblog.autowire.Person">
             *         <constructor-arg index="0" value="xiaoming"/>
             *         <constructor-arg index="1" value="1"/>
             *         <constructor-arg index="2" value="man"/>
             *     </bean>
             *
             * 此時 minNrOfArgs = maxIndex + 1 = 2 + 1 = 3,除了計算 minNrOfArgs,
             * 下面的方法還會將 cargs 中的參數數據轉存到 resolvedValues 中
             */
            minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
        }
   //這部分總結:獲得注入方式和構造函數參數

        // 獲取構造方法列表
        Constructor<?>[] candidates = chosenCtors;
        if (candidates == null) {
            //傳入的構造器爲空,獲得bean的Class對象
            Class<?> beanClass = mbd.getBeanClass();
            try {
                //獲取所有的構造器
                //getDeclaredConstructors:獲得public和private修飾的
                //getConstructors:獲得public修飾的
                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構造方法之前
        //參數數量多的構造方法放在前面
        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 分支的用途是:若匹配到到合適的構造方法了,提前結束 for 循環
             * constructorToUse != null 這個條件比較好理解,下面分析一下條件 argsToUse.length > paramTypes.length:
             * 前面說到 AutowireUtils.sortConstructors(candidates) 用於對構造方法進行
             * 排序,排序規則如下:
             *   1. 具有 public 訪問權限的構造方法排在非 public 構造方法前
             *   2. 參數數量多的構造方法排在前面
             *
             * 假設現在有一組構造方法按照上面的排序規則進行排序,排序結果如下(省略參數名稱):
             *
             *   1. public Hello(Object, Object, Object)
             *   2. public Hello(Object, Object)
             *   3. public Hello(Object)
             *   4. protected Hello(Integer, Object, Object, Object)
             *   5. protected Hello(Integer, Object, Object)
             *   6. protected Hello(Integer, Object)
             *
             * argsToUse = [num1, obj2],可以匹配上的構造方法2和構造方法6。由於構造方法2有
             * 更高的訪問權限,所以沒理由不選他(儘管後者在參數類型上更加匹配)。由於構造方法3
             * 參數數量 < argsToUse.length,參數數量上不匹配,也不應該選。所以 
             * argsToUse.length > paramTypes.length 這個條件用途是:在條件 
             * constructorToUse != null 成立的情況下,通過判斷參數數量與參數值數量
             * (argsToUse.length)是否一致,來決定是否提前終止構造方法匹配邏輯。
             */
            if (constructorToUse != null && argsToUse.length > paramTypes.length) {
                break;
            }

            /*
             * 構造方法參數數量低於配置的參數數量,則忽略當前構造方法,並重試。比如 
             * argsToUse = [obj1, obj2, obj3, obj4],上面的構造方法列表中,
             * 構造方法1、2和3顯然不是合適選擇,忽略之。
             */
            if (paramTypes.length < minNrOfArgs) {
                continue;
            }
           
            //從註解上獲得構造參數名稱
            ArgumentsHolder argsHolder;
            if (resolvedValues != null) {
                try {
                    /*
                     * 判斷否則方法是否有 ConstructorProperties 註解,若有,則取註解中的
                     * 值。比如下面的代碼:
                     * 
                     *  public class Persion {
                     *      private String name;
                     *      private Integer age;
                     *
                     *      @ConstructorProperties(value = {"coolblog", "20"})
                     *      public Persion(String name, Integer age) {
                     *          this.name = name;
                     *          this.age = age;
                     *      }
                     * }
                     */
                    String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
                    if (paramNames == null) {
                        ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
                        if (pnd != null) {
                            /*
                             * 獲取構造方法參數名稱列表,比如有這樣一個構造方法:
                             *   public Person(String name, int age, String sex)
                             *   
                             * 調用 getParameterNames 方法返回 paramNames = [name, age, sex]
                             */
                            paramNames = pnd.getParameterNames(candidate);
                        }
                    }
               //上面是獲得構造方法上的參數名稱

                    /* 
                     * 創建參數值列表,返回 argsHolder 會包含進行類型轉換後的參數值,比如下
                     * 面的配置:
                     *
                     *     <bean id="persion" class="xyz.coolblog.autowire.Person">
                     *         <constructor-arg name="name" value="xiaoming"/>
                     *         <constructor-arg name="age" value="1"/>
                     *         <constructor-arg name="sex" value="man"/>
                     *     </bean>
                     *
                     * Person 的成員變量 age 是 Integer 類型的,但由於在 Spring 配置中
                     * 只能配成 String 類型,所以這裏要進行類型轉換。
                     */
                   //根據已經獲得參數類型,參數名稱和構造參數類創建構造器用的構造參數數組
                    argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
                            getUserDeclaredConstructor(candidate), autowiring);
                }
                catch (UnsatisfiedDependencyException ex) {
                    if (this.beanFactory.logger.isTraceEnabled()) {
                        this.beanFactory.logger.trace(
                                "Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
                    }
                    if (causes == null) {
                        causes = new LinkedList<UnsatisfiedDependencyException>();
                    }
                    causes.add(ex);
                    continue;
                }
            }
            else {
                if (paramTypes.length != explicitArgs.length) {
                    continue;
                }
                argsHolder = new ArgumentsHolder(explicitArgs);
            }

            /*
             * 計算參數值(argsHolder.arguments)每個參數類型與構造方法參數列表
             * (paramTypes)中參數的類型差異量,差異量越大表明參數類型差異越大。參數類型差異
             * 越大,表明當前構造方法並不是一個最合適的候選項。引入差異量(typeDiffWeight)
             * 變量目的:是將候選構造方法的參數列表類型與參數值列表類型的差異進行量化,通過量化
             * 後的數值篩選出最合適的構造方法。
             * 
             * 講完差異量,再來說說 mbd.isLenientConstructorResolution() 條件。
             * 官方的解釋是:返回構造方法的解析模式,有寬鬆模式(lenient mode)和嚴格模式
             * 嚴格模式:構造解析函數時,必須所有進行匹配,否則拋出異常
             * 寬鬆模式:使用最接近的模式進行匹配              
             */
            int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
                    argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
            if (typeDiffWeight < minTypeDiffWeight) {
                constructorToUse = candidate;
                argsHolderToUse = argsHolder;
                argsToUse = argsHolder.arguments;
                minTypeDiffWeight = typeDiffWeight;
                ambiguousConstructors = null;
            }
            /* 
             * 如果兩個構造方法與參數值類型列表之間的差異量一致,那麼這兩個方法都可以作爲
             * 候選項,這個時候就出現歧義了,這裏先把有歧義的構造方法放入 
             * ambiguousConstructors 集合中
             */
            else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
                if (ambiguousConstructors == null) {
                    ambiguousConstructors = new LinkedHashSet<Constructor<?>>();
                    ambiguousConstructors.add(constructorToUse);
                }
                ambiguousConstructors.add(candidate);
            }
        }

        // 若上面未能篩選出合適的構造方法,這裏將拋出 BeanCreationException 異常
        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)");
        }
        /*
         * 如果 constructorToUse != null,且 ambiguousConstructors 也不爲空,表明解析
         * 出了多個的合適的構造方法,此時就出現歧義了。Spring 不會擅自決定使用哪個構造方法,
         * 所以拋出異常。
         */
        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);
        }

        if (explicitArgs == null) {
            /*
             * 緩存相關信息,比如:
             *   1. 已解析出的構造方法對象 resolvedConstructorOrFactoryMethod
             *   2. 構造方法參數列表是否已解析標誌 constructorArgumentsResolved
             *   3. 參數值列表 resolvedConstructorArguments 或 preparedConstructorArguments
             *
             * 這些信息可用在其他地方,用於進行快捷判斷
             */
            argsHolderToUse.storeCache(mbd, constructorToUse);
        }
    }

    try {
        Object beanInstance;

        if (System.getSecurityManager() != null) {
            final Constructor<?> ctorToUse = constructorToUse;
            final Object[] argumentsToUse = argsToUse;
            beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
                @Override
                public Object run() {
                    return beanFactory.getInstantiationStrategy().instantiate(
                            mbd, beanName, beanFactory, ctorToUse, argumentsToUse);
                }
            }, beanFactory.getAccessControlContext());
        }
        else {
            /*
             * 調用實例化策略創建實例,默認情況下使用反射創建實例。如果 bean 的配置信息中
             * 包含 lookup-method 和 replace-method,則通過 CGLIB 增強 bean 實例
             */
            beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(
                    mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
        }

        // 設置 beanInstance 到 BeanWrapperImpl 對象中
        bw.setBeanInstance(beanInstance);
        return bw;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "Bean instantiation via constructor failed", ex);
    }
}

該方法的核心邏輯是根據參數值類型篩選合適的構造方法。解析出合適的構造方法後,剩下的工作就是構建 bean 對象了,這個工作交給了實例化策略去做。下面羅列一下這個方法的工作流程吧:

  1. 創建 BeanWrapperImpl 對象
  2. 構造函數參數的確定(有下面三種確定方式),並算出 minNrOfArgs。
    1. 根據explicitArgs參數判斷。如果傳入的這個參數不爲空,可以直接確定參數。(因爲explicitArgs參數是在調用Bean的時候用戶指定的,在BeanFactory類中存在這樣的方法:Object getBean(String name,Object... args) throws BeansException 在獲取bean的時候,用戶不但指定bean的名稱還可以指定bean所對應類的構造函數或者工廠方法的參數,主要用於靜態工廠方法的調用,而這裏需要給定完全匹配的參數,所以,便可以判斷,如果傳入的參數explicitArgs不爲空,則可以確定構造函數的參數就是它。)
    2. 緩存中獲取。確定參數的辦法之前如果已經分析過,也就是說構造函數參數已經記錄在緩存中,那麼便可以直接拿來使用。要注意的是,在緩存中緩存的可能是參數的最終類型也可能是參數的初始類型,例如:構造函數參數要求的是int類型,但是原始參數是String類型的,那麼即便在緩存中得到了參數,也要經過類型轉換器的過濾以確保參數類型域對應的構造函數參數類型完全對應。
    3. 配置文件的獲取。以上兩種情況都不可行的時候,分析從獲取配置文件中配置的構造函數信息開始。可以通過調用 mbd.getConstructorArgumentValues()來獲取配置的構造函數信息。有了配置中的信息就可以獲取對應的參數值信息,獲取參數值信息包括直接指定值。如:直接指定構造函數中某個值爲原始類型String類型,或者是一個對其他bean的引用,而這一處理委託給resolvedConstructorArguments方法,並返回能解析到的參數的個數。
  3. 構造函數的確定,並排序。根據構造函數參數在所有構造函數中鎖定對應的構造函數,而匹配的方法就是根據參數個數匹配,所以在匹配之前需要對構造函數按照public構造函數優先參數數量降序,非public構造函數優先參數數量降序。這樣可以在遍歷的情況下迅速判斷排在後面的構造函數參數是否符合條件。
  4. 遍歷排序好的構造方法列表,篩選合適的構造方法
    1. 獲取構造方法參數列表中每個參數的名稱。由於在配置文件中並不是唯一限制使用參數位置索引的方式去創建,同樣還支持指定參數名稱設定參數值的情況。一種是通過註解的方式直接獲取,另一種就是使用Spring提供的工具類ParameterNameDiscoverer來獲取。
    2. 再次解析參數,此次解析會將value 屬性值進行類型轉換,由 String 轉爲合適的類型。構造函數,參數名稱,參數類型,參數值都可以確定後就可以鎖定構造函數以及轉換對應的參數類型。
    3. 計算構造方法參數列表與參數值列表之間的類型差異量,以篩選出更爲合適的構造方法(構造函數不確定性的驗證)。
  5. 緩存已篩選出的構造方法以及參數值列表,若再次創建 bean 實例時,可直接使用,無需再次進行篩選
  6. 使用初始化策略創建 bean 對象
  7. 將 bean 對象放入 BeanWrapperImpl 對象中,並返回該對象

2.3 通過默認構造方法創建 bean 對象

經歷了帶有參數的構造函數的實例構造,下來理解不帶參數的構造函數實例化:

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
    try {
        Object beanInstance;
        final BeanFactory parent = this;
        // if 條件分支裏的一大坨是 Java 安全相關的代碼,可以忽略,直接看 else 分支
        if (System.getSecurityManager() != null) {
            beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
                @Override
                public Object run() {
                    return getInstantiationStrategy().instantiate(mbd, beanName, parent);
                }
            }, getAccessControlContext());
        }
        else {
            /*
             * 調用實例化策略創建實例,默認情況下使用反射創建對象。如果 bean 的配置信息中
             * 包含 lookup-method 和 replace-method,則通過 CGLIB 創建 bean 對象
             */
            beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
        }
        // 創建 BeanWrapperImpl 對象
        BeanWrapper bw = new BeanWrapperImpl(beanInstance);
        initBeanWrapper(bw);
        return bw;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
    }
}

2.4 實例化策略

經過前面的分析,我們已經得到了足夠實例化的所有相關信息,完全可以使用最簡單的反射方法直接反射來構造實例對象,但是Spring並沒有這樣做,而是調用了一個方法來實例化:

public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
    // 檢測 bean 配置中是否配置了 lookup-method 或 replace-method,若配置了,則需使用 CGLIB 構建 bean 對象
    if (bd.getMethodOverrides().isEmpty()) {
        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(new PrivilegedExceptionAction<Constructor<?>>() {
                            @Override
                            public Constructor<?> run() throws Exception {
                                return clazz.getDeclaredConstructor((Class[]) null);
                            }
                        });
                    }
                    else {
                        // 獲取默認構造方法
                        constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
                    }
                    // 設置 resolvedConstructorOrFactoryMethod
                    bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                }
                catch (Throwable ex) {
                    throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                }
            }
        }
        // 通過無參構造方法創建 bean 對象
        return BeanUtils.instantiateClass(constructorToUse);
    }
    else {
        // 使用 GCLIG 創建 bean 對象
        return instantiateWithMethodInjection(bd, beanName, owner);
    }
}

程序中,首先判斷如果beanDefinition.getMethodOverrides()爲空的話也就是用戶沒有使用lookup和replace的配置方法,那麼直接使用反射方式,簡單便捷,但是如果使用了這兩個特性,就必須要使用動態代理的方式將包含兩個特性對應的邏輯的攔截增強器設置進去,這樣纔可以保證在調用方法的時候會被相應的攔截器增強,返回值包含攔截器的代理實例。

通過無參構造方法剛創建 bean 對象的代碼:

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
    Assert.notNull(ctor, "Constructor must not be null");
    try {
        // 設置構造方法爲可訪問
        ReflectionUtils.makeAccessible(ctor);
        // 通過反射創建 bean 實例,這裏的 args 是一個沒有元素的空數組
        return ctor.newInstance(args);
    }
    catch (InstantiationException ex) {
        throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
    }
    catch (IllegalAccessException ex) {
        throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
    }
    catch (IllegalArgumentException ex) {
        throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
    }
    catch (InvocationTargetException ex) {
        throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
    }
}

 

參考:創建原始 bean 對象

《Spring源碼深度解析》

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