歡迎光臨
每天分享高質量文章

【死磕 Spring】—– IOC 之 Factory 實體化 bean

精品專欄

 

原文出自:http://cmsblogs.com

這篇我們關註建立 bean 過程中的第一個步驟:實體化 bean,對應的方法為: createBeanInstance(),如下:

  1.   protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {

  2.        // 解析 bean,將 bean 類名解析為 class 取用

  3.        Class> beanClass = resolveBeanClass(mbd, beanName);

  4.        if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {

  5.            throw new BeanCreationException(mbd.getResourceDescription(), beanName,

  6.                    "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());

  7.        }

  8.        // 如果存在 Supplier 回呼,則使用給定的回呼方法初始化策略

  9.        Supplier> instanceSupplier = mbd.getInstanceSupplier();

  10.        if (instanceSupplier != null) {

  11.            return obtainFromSupplier(instanceSupplier, beanName);

  12.        }

  13.        // 如果工廠方法不為空,則使用工廠方法初始化策略

  14.        if (mbd.getFactoryMethodName() != null)  {

  15.            return instantiateUsingFactoryMethod(beanName, mbd, args);

  16.        }

  17.        boolean resolved = false;

  18.        boolean autowireNecessary = false;

  19.        if (args == null) {

  20.            // constructorArgumentLock 建構式的常用鎖

  21.            synchronized (mbd.constructorArgumentLock) {

  22.                // 如果已快取的解析的建構式或者工廠方法不為空,則可以利用建構式解析

  23.                // 因為需要根據引數確認到底使用哪個建構式,該過程比較消耗效能,所有採用快取機制

  24.                if (mbd.resolvedConstructorOrFactoryMethod != null) {

  25.                    resolved = true;

  26.                    autowireNecessary = mbd.constructorArgumentsResolved;

  27.                }

  28.            }

  29.        }

  30.        // 已經解析好了,直接註入即可

  31.        if (resolved) {

  32.            // 自動註入,呼叫建構式自動註入

  33.            if (autowireNecessary) {

  34.                return autowireConstructor(beanName, mbd, null, null);

  35.            }

  36.            else {

  37.                // 使用預設建構式構造

  38.                return instantiateBean(beanName, mbd);

  39.            }

  40.        }

  41.        // 確定解析的建構式

  42.        // 主要是檢查已經註冊的 SmartInstantiationAwareBeanPostProcessor

  43.        Constructor>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);

  44.        if (ctors != null ||

  45.                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||

  46.                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {

  47.            // 建構式自動註入

  48.            return autowireConstructor(beanName, mbd, ctors, args);

  49.        }

  50.        //使用預設建構式註入

  51.        return instantiateBean(beanName, mbd);

  52.    }

實體化 bean 是一個複雜的過程,其主要的邏輯為:

  • 如果存在 Supplier 回呼,則呼叫 obtainFromSupplier() 進行初始化

  • 如果存在工廠方法,則使用工廠方法進行初始化

  • 首先判斷快取,如果快取中存在,即已經解析過了,則直接使用已經解析了的,根據 constructorArgumentsResolved 引數來判斷是使用建構式自動註入還是預設建構式

  • 如果快取中沒有,則需要先確定到底使用哪個建構式來完成解析工作,因為一個類有多個建構式,每個建構式都有不同的構造引數,所以需要根據引數來鎖定建構式並完成初始化,如果存在引數則使用相應的帶有引數的建構式,否則使用預設建構式。

下麵就上面四種情況做分別說明。

obtainFromSupplier()

  1. Supplier> instanceSupplier = mbd.getInstanceSupplier();

  2. if (instanceSupplier != null) {

  3.    return obtainFromSupplier(instanceSupplier, beanName);

  4. }

首先從 BeanDefinition 中獲取 Supplier,如果不為空,則呼叫 obtainFromSupplier() 。那麼 Supplier 是什麼呢?在這之前也沒有提到過這個欄位。

  1. public interface Supplier<T> {

  2.    T get();

  3. }

Supplier 介面僅有一個功能性的 get(),該方法會傳回一個 T 型別的物件,有點兒類似工廠方法。這個介面有什麼作用?用於指定建立 bean 的回呼,如果我們設定了這樣的回呼,那麼其他的建構式或者工廠方法都會沒有用。在什麼設定該引數呢?Spring 提供了相應的 setter 方法,如下:

  1.    public void setInstanceSupplier(@Nullable Supplier> instanceSupplier) {

  2.        this.instanceSupplier = instanceSupplier;

  3.    }

在構造 BeanDefinition 的時候設定了該值,如下(以 RootBeanDefinition 為例):

  1.    public <T> RootBeanDefinition(@Nullable Class<T> beanClass, String scope, @Nullable Supplier<T> instanceSupplier) {

  2.        super();

  3.        setBeanClass(beanClass);

  4.        setScope(scope);

  5.        setInstanceSupplier(instanceSupplier);

  6.    }

如果設定了 instanceSupplier 則呼叫 obtainFromSupplier() 完成 bean 的初始化,如下:

  1.    protected BeanWrapper obtainFromSupplier(Supplier> instanceSupplier, String beanName) {

  2.        String outerBean = this.currentlyCreatedBean.get();

  3.        this.currentlyCreatedBean.set(beanName);

  4.        Object instance;

  5.        try {

  6.          // 呼叫 Supplier 的 get(),傳回一個物件

  7.            instance = instanceSupplier.get();

  8.        }

  9.        finally {

  10.            if (outerBean != null) {

  11.                this.currentlyCreatedBean.set(outerBean);

  12.            }

  13.            else {

  14.                this.currentlyCreatedBean.remove();

  15.            }

  16.        }

  17.        // 根據物件構造 BeanWrapper 物件

  18.        BeanWrapper bw = new BeanWrapperImpl(instance);

  19.        // 初始化 BeanWrapper

  20.        initBeanWrapper(bw);

  21.        return bw;

  22.    }

程式碼很簡單,呼叫 呼叫 Supplier 的 get() 方法,獲得一個 bean 實體物件,然後根據該實體物件構造一個 BeanWrapper 物件 bw,最後初始化該物件。有關於 BeanWrapper 後面專門出文講解。

instantiateUsingFactoryMethod()

如果存在工廠方法,則呼叫 instantiateUsingFactoryMethod() 完成 bean 的初始化工作(方法實現比較長,細節比較複雜,各位就硬著頭皮看吧)。

  1.    protected BeanWrapper instantiateUsingFactoryMethod(

  2.            String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {

  3.        return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);

  4.    }

構造一個 ConstructorResolver 物件,然後呼叫其 instantiateUsingFactoryMethod() 方法。ConstructorResolver 是構造方法或者工廠類初始化 bean 的委託類。

  1.      public BeanWrapper instantiateUsingFactoryMethod(

  2.            final String beanName, final RootBeanDefinition mbd, @Nullable final Object[] explicitArgs) {

  3.        // 構造 BeanWrapperImpl 物件

  4.        BeanWrapperImpl bw = new BeanWrapperImpl();

  5.        // 初始化 BeanWrapperImpl

  6.        // 向BeanWrapper物件中新增 ConversionService 物件和屬性編輯器 PropertyEditor 物件

  7.        //

  8.        this.beanFactory.initBeanWrapper(bw);

  9.        Object factoryBean;

  10.        Class> factoryClass;

  11.        boolean isStatic;

  12.        // 工廠名不為空

  13.        String factoryBeanName = mbd.getFactoryBeanName();

  14.        if (factoryBeanName != null) {

  15.            if (factoryBeanName.equals(beanName)) {

  16.                throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,

  17.                        "factory-bean reference points back to the same bean definition");

  18.            }

  19.            // 獲取工廠實體

  20.            factoryBean = this.beanFactory.getBean(factoryBeanName);

  21.            if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {

  22.                throw new ImplicitlyAppearedSingletonException();

  23.            }

  24.            factoryClass = factoryBean.getClass();

  25.            isStatic = false;

  26.        }

  27.        else {

  28.            // 工廠名為空,則其可能是一個靜態工廠

  29.            // 靜態工廠建立bean,必須要提供工廠的全類名

  30.            if (!mbd.hasBeanClass()) {

  31.                throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,

  32.                        "bean definition declares neither a bean class nor a factory-bean reference");

  33.            }

  34.            factoryBean = null;

  35.            factoryClass = mbd.getBeanClass();

  36.            isStatic = true;

  37.        }

  38.        // 工廠方法

  39.        Method factoryMethodToUse = null;

  40.        ConstructorResolver.ArgumentsHolder argsHolderToUse = null;

  41.        // 引數

  42.        Object[] argsToUse = null;

  43.        // 工廠方法的引數

  44.        // 如果指定了構造引數則直接使用

  45.        // 在呼叫 getBean 方法的時候指定了方法引數

  46.        if (explicitArgs != null) {

  47.            argsToUse = explicitArgs;

  48.        }

  49.        else {

  50.            // 沒有指定,則嘗試從配置檔案中解析

  51.            Object[] argsToResolve = null;

  52.            // 首先嘗試從快取中獲取

  53.            synchronized (mbd.constructorArgumentLock) {

  54.                // 獲取快取中的建構式或者工廠方法

  55.                factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;

  56.                if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {

  57.                    // 獲取快取中的構造引數

  58.                    argsToUse = mbd.resolvedConstructorArguments;

  59.                    if (argsToUse == null) {

  60.                        // 獲取快取中的建構式引數的包可見欄位

  61.                        argsToResolve = mbd.preparedConstructorArguments;

  62.                    }

  63.                }

  64.            }

  65.            // 快取中存在,則解析儲存在 BeanDefinition 中的引數

  66.            // 如給定方法的建構式 A(int ,int ),則透過此方法後就會把配置檔案中的("1","1")轉換為 (1,1)

  67.            // 快取中的值可能是原始值也有可能是最終值

  68.            if (argsToResolve != null) {

  69.                argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve);

  70.            }

  71.        }

  72.        //

  73.        if (factoryMethodToUse == null || argsToUse == null) {

  74.            // 獲取工廠方法的類全名稱

  75.            factoryClass = ClassUtils.getUserClass(factoryClass);

  76.            // 獲取所有待定方法

  77.            Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);

  78.            // 檢索所有方法,這裡是對方法進行過濾

  79.            List<Method> candidateSet = new ArrayList<>();

  80.            for (Method candidate : rawCandidates) {

  81.                // 如果有static 且為工廠方法,則新增到 candidateSet 中

  82.                if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {

  83.                    candidateSet.add(candidate);

  84.                }

  85.            }

  86.            Method[] candidates = candidateSet.toArray(new Method[0]);

  87.            // 排序建構式

  88.            // public 建構式優先引數數量降序,非public 建構式引數數量降序

  89.            AutowireUtils.sortFactoryMethods(candidates);

  90.            // 用於承載解析後的建構式引數的值

  91.            ConstructorArgumentValues resolvedValues = null;

  92.            boolean autowiring = (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);

  93.            int minTypeDiffWeight = Integer.MAX_VALUE;

  94.            Set<Method> ambiguousFactoryMethods = null;

  95.            int minNrOfArgs;

  96.            if (explicitArgs != null) {

  97.                minNrOfArgs = explicitArgs.length;

  98.            }

  99.            else {

  100.                // getBean() 沒有傳遞引數,則需要解析儲存在 BeanDefinition 建構式中指定的引數

  101.                if (mbd.hasConstructorArgumentValues()) {

  102.                    // 建構式的引數

  103.                    ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();

  104.                    resolvedValues = new ConstructorArgumentValues();

  105.                    // 解析建構式的引數

  106.                    // 將該 bean 的建構式引數解析為 resolvedValues 物件,其中會涉及到其他 bean

  107.                    minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);

  108.                }

  109.                else {

  110.                    minNrOfArgs = 0;

  111.                }

  112.            }

  113.            LinkedList<UnsatisfiedDependencyException> causes = null;

  114.            for (Method candidate : candidates) {

  115.                // 方法體的引數

  116.                Class>[] paramTypes = candidate.getParameterTypes();

  117.                if (paramTypes.length >= minNrOfArgs) {

  118.                    // 儲存引數的物件

  119.                    ArgumentsHolder argsHolder;

  120.                    // getBean()傳遞了引數

  121.                    if (explicitArgs != null){

  122.                        // 顯示給定引數,引數長度必須完全匹配

  123.                        if (paramTypes.length != explicitArgs.length) {

  124.                            continue;

  125.                        }

  126.                        // 根據引數建立引數持有者

  127.                        argsHolder = new ArgumentsHolder(explicitArgs);

  128.                    }

  129.                    else {

  130.                        // 為提供引數,解析構造引數

  131.                        try {

  132.                            String[] paramNames = null;

  133.                            // 獲取 ParameterNameDiscoverer 物件

  134.                            // ParameterNameDiscoverer 是用於解析方法和建構式的引數名稱的介面,為引數名稱探測器

  135.                            ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();

  136.                            if (pnd != null) {

  137.                                // 獲取指定建構式的引數名稱

  138.                                paramNames = pnd.getParameterNames(candidate);

  139.                            }

  140.                            // 在已經解析的建構式引數值的情況下,建立一個引數持有者物件

  141.                            argsHolder = createArgumentArray(

  142.                                    beanName, mbd, resolvedValues, bw, paramTypes, paramNames, candidate, autowiring);

  143.                        }

  144.                        catch (UnsatisfiedDependencyException ex) {

  145.                            if (this.beanFactory.logger.isTraceEnabled()) {

  146.                                this.beanFactory.logger.trace("Ignoring factory method [" + candidate +

  147.                                        "] of bean '" + beanName + "': " + ex);

  148.                            }

  149.                            if (causes == null) {

  150.                                causes = new LinkedList<>();

  151.                            }

  152.                            causes.add(ex);

  153.                            continue;

  154.                        }

  155.                    }

  156.                    // isLenientConstructorResolution 判斷解析建構式的時候是否以寬鬆樣式還是嚴格樣式

  157.                    // 嚴格樣式:解析建構式時,必須所有的都需要匹配,否則丟擲異常

  158.                    // 寬鬆樣式:使用具有"最接近的樣式"進行匹配

  159.                    // typeDiffWeight:型別差異權重

  160.                    int typeDiffWeight = (mbd.isLenientConstructorResolution() ?

  161.                            argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));

  162.                    // 代表最接近的型別匹配,則選擇作為建構式

  163.                    if (typeDiffWeight < minTypeDiffWeight) {

  164.                        factoryMethodToUse = candidate;

  165.                        argsHolderToUse = argsHolder;

  166.                        argsToUse = argsHolder.arguments;

  167.                        minTypeDiffWeight = typeDiffWeight;

  168.                        ambiguousFactoryMethods = null;

  169.                    }

  170.                    // 如果具有相同引數數量的方法具有相同的型別差異權重,則收集此型別選項

  171.                    // 但是,僅在非寬鬆建構式解析樣式下執行該檢查,並顯式忽略重寫方法(具有相同的引數簽名)

  172.                    else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&

  173.                            !mbd.isLenientConstructorResolution() &&

  174.                            paramTypes.length == factoryMethodToUse.getParameterCount() &&

  175.                            !Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {

  176.                        // 查詢到多個可匹配的方法

  177.                        if (ambiguousFactoryMethods == null) {

  178.                            ambiguousFactoryMethods = new LinkedHashSet<>();

  179.                            ambiguousFactoryMethods.add(factoryMethodToUse);

  180.                        }

  181.                        ambiguousFactoryMethods.add(candidate);

  182.                    }

  183.                }

  184.            }

  185.            // 沒有可執行的工廠方法,丟擲異常

  186.            if (factoryMethodToUse == null) {

  187.                if (causes != null) {

  188.                    UnsatisfiedDependencyException ex = causes.removeLast();

  189.                    for (Exception cause : causes) {

  190.                        this.beanFactory.onSuppressedException(cause);

  191.                    }

  192.                    throw ex;

  193.                }

  194.                List<String> argTypes = new ArrayList<>(minNrOfArgs);

  195.                if (explicitArgs != null) {

  196.                    for (Object arg : explicitArgs) {

  197.                        argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null");

  198.                    }

  199.                }

  200.                else if (resolvedValues != null){

  201.                    Set<ConstructorArgumentValues.ValueHolder> valueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount());

  202.                    valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values());

  203.                    valueHolders.addAll(resolvedValues.getGenericArgumentValues());

  204.                    for (ConstructorArgumentValues.ValueHolder value : valueHolders) {

  205.                        String argType = (value.getType() != null ? ClassUtils.getShortName(value.getType()) :

  206.                                (value.getValue() != null ? value.getValue().getClass().getSimpleName() : "null"));

  207.                        argTypes.add(argType);

  208.                    }

  209.                }

  210.                String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);

  211.                throw new BeanCreationException(mbd.getResourceDescription(), beanName,

  212.                        "No matching factory method found: " +

  213.                                (mbd.getFactoryBeanName() != null ?

  214.                                        "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +

  215.                                "factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +

  216.                                "Check that a method with the specified name " +

  217.                                (minNrOfArgs > 0 ? "and arguments " : "") +

  218.                                "exists and that it is " +

  219.                                (isStatic ? "static" : "non-static") + ".");

  220.            }

  221.            else if (void.class == factoryMethodToUse.getReturnType()) {

  222.                throw new BeanCreationException(mbd.getResourceDescription(), beanName,

  223.                        "Invalid factory method '" + mbd.getFactoryMethodName() +

  224.                                "': needs to have a non-void return type!");

  225.            }

  226.            else if (ambiguousFactoryMethods != null) {

  227.                throw new BeanCreationException(mbd.getResourceDescription(), beanName,

  228.                        "Ambiguous factory method matches found in bean '" + beanName + "' " +

  229.                                "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +

  230.                                ambiguousFactoryMethods);

  231.            }

  232.            if (explicitArgs == null && argsHolderToUse != null) {

  233.                // 將解析的建構式加入快取

  234.                argsHolderToUse.storeCache(mbd, factoryMethodToUse);

  235.            }

  236.        }

  237.        try {

  238.            // 實體化 bean

  239.            Object beanInstance;

  240.            if (System.getSecurityManager() != null) {

  241.                final Object fb = factoryBean;

  242.                final Method factoryMethod = factoryMethodToUse;

  243.                final Object[] args = argsToUse;

  244.                // 透過執行工廠方法來建立bean示例

  245.                beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->

  246.                                beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, beanFactory, fb, factoryMethod, args),

  247.                        beanFactory.getAccessControlContext());

  248.            }

  249.            else {

  250.                // 透過執行工廠方法來建立bean示例

  251.                beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(

  252.                        mbd, beanName, this.beanFactory, factoryBean, factoryMethodToUse, argsToUse);

  253.            }

  254.            // 包裝為 BeanWraper 物件

  255.            bw.setBeanInstance(beanInstance);

  256.            return bw;

  257.        }

  258.        catch (Throwable ex) {

  259.            throw new BeanCreationException(mbd.getResourceDescription(), beanName,

  260.                    "Bean instantiation via factory method failed", ex);

  261.        }

  262.    }

instantiateUsingFactoryMethod() 方法體實在是太大了,處理細節感覺很複雜,LZ是硬著頭皮看完的,中間斷斷續續的。吐槽這裡的程式碼風格,完全不符合我們前面看的 Spring 程式碼風格。Spring 的一貫做法是將一個複雜邏輯進行拆分,分為多個細小的模組進行巢狀,每個模組負責一部分功能,模組與模組之間層層巢狀,上一層一般都是對下一層的總結和概括,這樣就會使得每一層的邏輯變得清晰易懂。

回歸到上面的方法體,雖然程式碼體量大,但是總體我們還是可看清楚這個方法要做的事情。一句話概括就是:確定工廠物件,然後獲取建構式和構造引數,最後呼叫 InstantiationStrategy 物件的 instantiate() 來建立 bean 實體。下麵我們就這個句概括的話進行拆分並詳細說明。

確定工廠物件

首先獲取工廠方法名,若工廠方法名不為空,則呼叫 beanFactory.getBean() 獲取工廠物件,若為空,則可能為一個靜態工廠,對於靜態工廠則必須提供工廠類的全類名,同時設定 factoryBean=null

構造引數確認

工廠物件確定後,則是確認構造引數。構造引數的確認主要分為三種情況:explicitArgs 引數、快取中獲取、配置檔案中解析。

explicitArgs 引數

explicitArgs 引數是我們呼叫 getBean() 時傳遞景來,一般該引數,該引數就是用於初始化 bean 時所傳遞的引數,如果該引數不為空,則可以確定建構式的引數就是它了。

快取中獲取

在該方法的最後,我們會發現這樣一段程式碼: argsHolderToUse.storeCache(mbd,factoryMethodToUse) ,這段程式碼主要是將建構式、構造引數儲存到快取中,如下:

  1.        public void storeCache(RootBeanDefinition mbd, Executable constructorOrFactoryMethod) {

  2.            synchronized (mbd.constructorArgumentLock) {

  3.                mbd.resolvedConstructorOrFactoryMethod = constructorOrFactoryMethod;

  4.                mbd.constructorArgumentsResolved = true;

  5.                if (this.resolveNecessary) {

  6.                    mbd.preparedConstructorArguments = this.preparedArguments;

  7.                }

  8.                else {

  9.                    mbd.resolvedConstructorArguments = this.arguments;

  10.                }

  11.            }

  12.        }

其中涉及到的幾個引數 constructorArgumentLock、resolvedConstructorOrFactoryMethod、constructorArgumentsResolved、resolvedConstructorArguments。這些引數都是跟建構式、建構式快取有關的。

  • constructorArgumentLock:建構式的快取鎖

  • resolvedConstructorOrFactoryMethod:快取已經解析的建構式或者工廠方法

  • constructorArgumentsResolved:標記欄位,標記建構式、引數已經解析了。預設為false

  • resolvedConstructorArguments:快取已經解析的建構式引數,包可見欄位

所以從快取中獲取就是提取這幾個引數的值,如下:

  1.            synchronized (mbd.constructorArgumentLock) {

  2.                // 獲取快取中的建構式或者工廠方法

  3.                factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;

  4.                if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {

  5.                    // 獲取快取中的構造引數

  6.                    argsToUse = mbd.resolvedConstructorArguments;

  7.                    if (argsToUse == null) {

  8.                        // 獲取快取中的建構式引數的包可見欄位

  9.                        argsToResolve = mbd.preparedConstructorArguments;

  10.                    }

  11.                }

  12.            }

如果快取中存在構造引數,則需要呼叫 resolvePreparedArguments() 方法進行轉換,因為快取中的值有可能是最終值也有可能不是最終值,比如我們建構式中的型別為 Integer 型別的 1 ,但是原始的引數型別有可能是 String 型別的 1 ,所以即便是從快取中得到了構造引數也需要經過一番的型別轉換確保引數型別完全對應。

配置檔案中解析

即沒有透過傳遞引數的方式傳遞構造引數,快取中也沒有,那就只能透過解析配置檔案獲取構造引數了。

在 bean 解析類的博文中我們瞭解了,配置檔案中的資訊都會轉換到 BeanDefinition 實體物件中,所以配置檔案中的引數可以直接透過 BeanDefinition 物件獲取。程式碼如下:

  1. if (mbd.hasConstructorArgumentValues()) {

  2.    // 建構式的引數

  3.    ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();

  4.    resolvedValues = new ConstructorArgumentValues();

  5.    // 解析建構式的引數

  6.    // 將該 bean 的建構式引數解析為 resolvedValues 物件,其中會涉及到其他 bean

  7.    minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);

  8. }

透過 BeanDefinition 的 getConstructorArgumentValues() 就可以獲取構造資訊了,有了構造資訊就可以獲取相關的引數值資訊了,獲取的引數資訊包括直接值和取用,這一步驟的處理交由 resolveConstructorArguments()完成,該方法會將構造引數資訊解析為 resolvedValues 物件 並傳回解析到的引數個數。

建構式

確定構造引數後,下一步則是確定建構式。第一步則是透過 getCandidateMethods() 獲取所有的構造方法,同時對構造方法進行刷選,然後在對其進行排序處理( AutowireUtils.sortFactoryMethods(candidates)),排序的主要目的是為了能夠更加方便的找到匹配的建構式,因為建構式的確認是根據引數個數確認的。排序的規則是:public 建構式優先引數數量降序、非 public 構造引數數量降序。

透過迭代 candidates(包含了所有要匹配的建構式)的方式,一次比較其引數,如果顯示提供了引數(explicitArgs != null),則直接比較兩者是否相等,如果相等則表示找到了,否則繼續比較。如果沒有顯示提供引數,則需要獲取 ParameterNameDiscoverer 物件,該物件為引數名稱探測器,主要用於發現方法和建構式的引數名稱。

將引數包裝成 ArgumentsHolder 物件,該物件用於儲存引數,我們稱之為引數持有者。當將物件包裝成 ArgumentsHolder 物件後,我們就可以透過它來進行建構式匹配,匹配分為嚴格樣式和寬鬆樣式。

  • 嚴格樣式:解析建構式時,必須所有引數都需要匹配,否則丟擲異常

  • 寬鬆樣式:使用具有"最接近的樣式"進行匹配

判斷的依據是根據 BeanDefinition 的 isLenientConstructorResolution 屬性(該引數是我們在構造 AbstractBeanDefinition 物件是傳遞的)來獲取型別差異權重(typeDiffWeight) 的。如果 typeDiffWeight<minTypeDiffWeight ,則代表“最接近的樣式”,選擇其作為建構式,否則只有兩者具有相同的引數數量且型別差異權重相等才會納入考慮範圍。

至此,建構式已經確認了。

建立 bean 實體

工廠物件、建構式、構造引數都已經確認了,則最後一步就是呼叫 InstantiationStrategy 物件的 instantiate() 來建立 bean 實體,如下:

  1. public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,

  2.            @Nullable Object factoryBean, final Method factoryMethod, @Nullable Object... args) {

  3.        try {

  4.            if (System.getSecurityManager() != null) {

  5.                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {

  6.                    ReflectionUtils.makeAccessible(factoryMethod);

  7.                    return null;

  8.                });

  9.            }

  10.            else {

  11.                ReflectionUtils.makeAccessible(factoryMethod);

  12.            }

  13.            Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();

  14.            try {

  15.                currentlyInvokedFactoryMethod.set(factoryMethod);

  16.                // 執行工廠方法,並傳回實體

  17.                Object result = factoryMethod.invoke(factoryBean, args);

  18.                if (result == null) {

  19.                    result = new NullBean();

  20.                }

  21.                return result;

  22.            }

  23.            finally {

  24.                if (priorInvokedFactoryMethod != null) {

  25.                    currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);

  26.                }

  27.                else {

  28.                    currentlyInvokedFactoryMethod.remove();

  29.                }

  30.            }

  31.        }

  32.        // 省略一波 catch

  33.    }

instantiate() 最核心的部分就是利用 Java 反射執行工廠方法並傳回建立好的實體,也就是這段程式碼:

  1. Object result = factoryMethod.invoke(factoryBean, args);

到這裡 instantiateUsingFactoryMethod() 已經分析完畢了,這裡 LZ 有些題外話需要說下,看原始碼真心是一個痛苦的過程,尤其是複雜的原始碼,比如這個方法我看了三天才弄清楚點皮毛,當然這裡跟 LZ 的智商有些關係(智商捉急 ┭┮﹏┭┮),寫這篇部落格也花了五天時間才寫完(最後截稿日為:2018.08.10 01:23:49),所以每一個堅持寫部落格的都是折翼的天使,值得各位尊敬

createBeanInstance() 還有兩個重要方法 autowireConstructor() 和 instantiateBean() ,由於篇幅問題,所以將這兩個方法放在下篇部落格分析。敬請期待!!!

【死磕 Spring】----- IOC 之分析各 scope 的 bean 建立

【死磕 Spring】----- IOC 之開啟 bean 的載入

【死磕 Spring】----- IOC 之從單例快取中獲取單例 bean

【死磕 Spring】----- IOC 之parentBeanFactory 與依賴處理

【死磕 Spring】----- IOC 之 IOC 初始化總結

END


>>>>>> 加群交流技術 <<<<<<

贊(0)

分享創造快樂

© 2024 知識星球   網站地圖