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

【死磕 Spring】—– IOC 之建構式實體化 bean

精品專欄

 

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

createBeanInstance() 用於實體化 bean,它會根據不同情況選擇不同的實體化策略來完成 bean 的初始化,主要包括:

  • Supplier 回呼: obtainFromSupplier()

  • 工廠方法初始化: instantiateUsingFactoryMethod()

  • 建構式自動註入初始化: autowireConstructor()

  • 預設建構式註入: instantiateBean()

在上篇部落格(【死磕 Spring】—– IOC 之 Factory 實體化 bean) 中分析了 Supplier 回呼和工廠方法初始化,這篇分析兩個建構式註入。

autowireConstructor()

這個初始化方法我們可以簡單理解為是帶有引數的初始化 bean 。程式碼段如下:

  1.   public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd,

  2.                                           @Nullable Constructor>[] chosenCtors, @Nullable final Object[] explicitArgs) {

  3.        // 封裝 BeanWrapperImpl  並完成初始化

  4.        BeanWrapperImpl bw = new BeanWrapperImpl();

  5.        this.beanFactory.initBeanWrapper(bw);

  6.        // 建構式

  7.        Constructor> constructorToUse = null;

  8.        // 構造引數

  9.        ArgumentsHolder argsHolderToUse = null;

  10.        Object[] argsToUse = null;

  11.        /*

  12.         * 確定構造引數

  13.         */

  14.        // 如果 getBean() 已經傳遞,則直接使用

  15.        if (explicitArgs != null) {

  16.            argsToUse = explicitArgs;

  17.        }

  18.        else {

  19.            /*

  20.             *  嘗試從快取中獲取

  21.             */

  22.            Object[] argsToResolve = null;

  23.            synchronized (mbd.constructorArgumentLock) {

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

  25.                constructorToUse = (Constructor>) mbd.resolvedConstructorOrFactoryMethod;

  26.                if (constructorToUse != null && mbd.constructorArgumentsResolved) {

  27.                    // 快取中的構造引數

  28.                    argsToUse = mbd.resolvedConstructorArguments;

  29.                    if (argsToUse == null) {

  30.                        argsToResolve = mbd.preparedConstructorArguments;

  31.                    }

  32.                }

  33.            }

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

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

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

  37.            if (argsToResolve != null) {

  38.                argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);

  39.            }

  40.        }

  41.        /*

  42.         * 沒有快取,則嘗試從配置檔案中獲取

  43.         */

  44.        if (constructorToUse == null) {

  45.            // 是否需要解析建構式

  46.            boolean autowiring = (chosenCtors != null ||

  47.                    mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);

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

  49.            ConstructorArgumentValues resolvedValues = null;

  50.            int minNrOfArgs;

  51.            if (explicitArgs != null) {

  52.                minNrOfArgs = explicitArgs.length;

  53.            }

  54.            else {

  55.                // 從 BeanDefinition 中獲取構造引數,也就是從配置檔案中提取構造引數

  56.                ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();

  57.                resolvedValues = new ConstructorArgumentValues();

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

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

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

  61.            }

  62.            /*

  63.             * 獲取指定的建構式

  64.             */

  65.            // 根據前面的判斷,chosenCtors 應該為 null

  66.            Constructor>[] candidates = chosenCtors;

  67.            if (candidates == null) {

  68.                // 獲取 bean 的 class

  69.                Class> beanClass = mbd.getBeanClass();

  70.                try {

  71.                    // 根據 class 獲取所有的建構式

  72.                    candidates = (mbd.isNonPublicAccessAllowed() ?

  73.                            beanClass.getDeclaredConstructors() : beanClass.getConstructors());

  74.                }

  75.                catch (Throwable ex) {

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

  77.                            "Resolution of declared constructors on bean Class [" + beanClass.getName() +

  78.                                    "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);

  79.                }

  80.            }

  81.            // 對建構式進行排序處理

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

  83.            AutowireUtils.sortConstructors(candidates);

  84.            // 最小引數型別權重

  85.            int minTypeDiffWeight = Integer.MAX_VALUE;

  86.            Set<Constructor>> ambiguousConstructors = null;

  87.            LinkedList<UnsatisfiedDependencyException> causes = null;

  88.            // 迭代所有建構式

  89.            for (Constructor> candidate : candidates) {

  90.                // 獲取該建構式的引數型別

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

  92.                // 如果已經找到選用的建構式或者需要的引數個數小於當前的建構式引數個數,則終止

  93.                // 因為已經按照引數個數降序排列了

  94.                if (constructorToUse != null && argsToUse.length > paramTypes.length) {

  95.                    break;

  96.                }

  97.                // 引數個數不等,繼續

  98.                if (paramTypes.length < minNrOfArgs) {

  99.                    continue;

  100.                }

  101.                // 引數持有者

  102.                ArgumentsHolder argsHolder;

  103.                // 有引數

  104.                if (resolvedValues != null) {

  105.                    try {

  106.                        // 註釋上獲取引數名稱

  107.                        String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);

  108.                        if (paramNames == null) {

  109.                            // 獲取建構式、方法引數的探測器

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

  111.                            if (pnd != null) {

  112.                                // 透過探測器獲取建構式的引數名稱

  113.                                paramNames = pnd.getParameterNames(candidate);

  114.                            }

  115.                        }

  116.                        // 根據建構式和構造引數建立引數持有者

  117.                        argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,

  118.                                getUserDeclaredConstructor(candidate), autowiring);

  119.                    }

  120.                    catch (UnsatisfiedDependencyException ex) {

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

  122.                            this.beanFactory.logger.trace(

  123.                                    "Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);

  124.                        }

  125.                        // Swallow and try next constructor.

  126.                        if (causes == null) {

  127.                            causes = new LinkedList<>();

  128.                        }

  129.                        causes.add(ex);

  130.                        continue;

  131.                    }

  132.                }

  133.                else {

  134.                    // 建構式沒有引數

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

  136.                        continue;

  137.                    }

  138.                    argsHolder = new ArgumentsHolder(explicitArgs);

  139.                }

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

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

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

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

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

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

  146.                // 如果它代表著當前最接近的匹配則選擇其作為建構式

  147.                if (typeDiffWeight < minTypeDiffWeight) {

  148.                    constructorToUse = candidate;

  149.                    argsHolderToUse = argsHolder;

  150.                    argsToUse = argsHolder.arguments;

  151.                    minTypeDiffWeight = typeDiffWeight;

  152.                    ambiguousConstructors = null;

  153.                }

  154.                else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {

  155.                    if (ambiguousConstructors == null) {

  156.                        ambiguousConstructors = new LinkedHashSet<>();

  157.                        ambiguousConstructors.add(constructorToUse);

  158.                    }

  159.                    ambiguousConstructors.add(candidate);

  160.                }

  161.            }

  162.            if (constructorToUse == null) {

  163.                if (causes != null) {

  164.                    UnsatisfiedDependencyException ex = causes.removeLast();

  165.                    for (Exception cause : causes) {

  166.                        this.beanFactory.onSuppressedException(cause);

  167.                    }

  168.                    throw ex;

  169.                }

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

  171.                        "Could not resolve matching constructor " +

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

  173.            }

  174.            else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {

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

  176.                        "Ambiguous constructor matches found in bean '" + beanName + "' " +

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

  178.                                ambiguousConstructors);

  179.            }

  180.            // 將建構式、構造引數儲存到快取中

  181.            if (explicitArgs == null) {

  182.                argsHolderToUse.storeCache(mbd, constructorToUse);

  183.            }

  184.        }

  185.        try {

  186.            // 獲取建立 bean 的策略

  187.            final InstantiationStrategy strategy = beanFactory.getInstantiationStrategy();

  188.            Object beanInstance;

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

  190.                final Constructor> ctorToUse = constructorToUse;

  191.                final Object[] argumentsToUse = argsToUse;

  192.                // 實體化 bean

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

  194.                                strategy.instantiate(mbd, beanName, beanFactory, ctorToUse, argumentsToUse),

  195.                        beanFactory.getAccessControlContext());

  196.            }

  197.            else {

  198.                // 實體化bean

  199.                beanInstance = strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);

  200.            }

  201.            // 將構造的 bean 加入到 BeanWrapper 實體中

  202.            bw.setBeanInstance(beanInstance);

  203.            return bw;

  204.        }

  205.        catch (Throwable ex) {

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

  207.                    "Bean instantiation via constructor failed", ex);

  208.        }

  209.    }

程式碼與 instantiateUsingFactoryMethod() 一樣,又長又難懂,但是如果理解了 instantiateUsingFactoryMethod() 初始化 bean 的過程,那麼 autowireConstructor() 也不存在什麼難的地方了,一句話概括:首先確定建構式引數、建構式,然後呼叫相應的初始化策略進行 bean 的初始化。關於如何確定建構式、構造引數,該部分邏輯和 instantiateUsingFactoryMethod() 基本一致,所以這裡不再重覆闡述了,具體過程請移步【死磕 Spring】----- IOC 之 Factory 實體化 bean,這裡我們重點分析初始化策略。

對於初始化策略,首先是獲取實體化 bean 的策略,如下:

  1. final InstantiationStrategy strategy = beanFactory.getInstantiationStrategy();

然後是呼叫其 instantiate()方法,該方法在 SimpleInstantiationStrategy 中實現,如下:

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

  2.        // 沒有改寫

  3.        // 直接使用反射實體化即可

  4.        if (!bd.hasMethodOverrides()) {

  5.            // 重新檢測獲取下建構式

  6.            // 該建構式是經過前面 N 多複雜過程確認的建構式

  7.            Constructor> constructorToUse;

  8.            synchronized (bd.constructorArgumentLock) {

  9.                // 獲取已經解析的建構式

  10.                constructorToUse = (Constructor>) bd.resolvedConstructorOrFactoryMethod;

  11.                // 如果為 null,從 class 中解析獲取,並設定

  12.                if (constructorToUse == null) {

  13.                    final Class> clazz = bd.getBeanClass();

  14.                    if (clazz.isInterface()) {

  15.                        throw new BeanInstantiationException(clazz, "Specified class is an interface");

  16.                    }

  17.                    try {

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

  19.                            constructorToUse = AccessController.doPrivileged(

  20.                                    (PrivilegedExceptionAction<Constructor>>) clazz::getDeclaredConstructor);

  21.                        }

  22.                        else {

  23.                            constructorToUse =    clazz.getDeclaredConstructor();

  24.                        }

  25.                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;

  26.                    }

  27.                    catch (Throwable ex) {

  28.                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);

  29.                    }

  30.                }

  31.            }

  32.            // 透過BeanUtils直接使用建構式物件實體化bean

  33.            return BeanUtils.instantiateClass(constructorToUse);

  34.        }

  35.        else {

  36.            // 生成CGLIB建立的子類物件

  37.            return instantiateWithMethodInjection(bd, beanName, owner);

  38.        }

  39.    }

如果該 bean 沒有配置 lookup-method、replaced-method 標簽或者 @Lookup 註解,則直接透過反射的方式實體化 bean 即可,方便快捷,但是如果存在需要改寫的方法或者動態替換的方法則需要使用 CGLIB 進行動態代理,因為可以在建立代理的同時將動態方法織入類中。

反射

呼叫工具類 BeanUtils 的 instantiateClass() 方法完成反射工作:

  1.    public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {

  2.        Assert.notNull(ctor, "Constructor must not be null");

  3.        try {

  4.            ReflectionUtils.makeAccessible(ctor);

  5.            return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?

  6.                    KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));

  7.        }

  8.        // 省略一些 catch

  9.    }

CGLIB

  1.    protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {

  2.        throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");

  3.    }

方法預設是沒有實現的,具體過程由其子類 CglibSubclassingInstantiationStrategy 實現:

  1.    protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {

  2.        return instantiateWithMethodInjection(bd, beanName, owner, null);

  3.    }

  4.    protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,

  5.            @Nullable Constructor> ctor, @Nullable Object... args) {

  6.        // 透過CGLIB生成一個子類物件

  7.        return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);

  8.    }

建立一個 CglibSubclassCreator 物件,呼叫其 instantiate() 方法生成其子類物件:

  1.    public Object instantiate(@Nullable Constructor> ctor, @Nullable Object... args) {

  2.        // 透過 Cglib 建立一個代理類

  3.        Class> subclass = createEnhancedSubclass(this.beanDefinition);

  4.        Object instance;

  5.        // 沒有建構式,透過 BeanUtils 使用預設建構式建立一個bean實體

  6.        if (ctor == null) {

  7.            instance = BeanUtils.instantiateClass(subclass);

  8.        }

  9.        else {

  10.            try {

  11.                // 獲取代理類對應的建構式物件,並實體化 bean

  12.                Constructor> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());

  13.                instance = enhancedSubclassConstructor.newInstance(args);

  14.            }

  15.            catch (Exception ex) {

  16.                throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),

  17.                        "Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);

  18.            }

  19.        }

  20.        // 為了避免memory leaks異常,直接在bean實體上設定回呼物件

  21.        Factory factory = (Factory) instance;

  22.        factory.setCallbacks(new Callback[] {NoOp.INSTANCE,

  23.                new CglibSubclassingInstantiationStrategy.LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),

  24.                new CglibSubclassingInstantiationStrategy.ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});

  25.        return instance;

  26.    }

到這類 CGLIB 的方式分析完畢了,當然這裡還沒有具體分析 CGLIB 生成子類的詳細過程,具體的過程等後續分析 AOP 的時候再詳細地介紹。

instantiateBean()

  1.   protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {

  2.        try {

  3.            Object beanInstance;

  4.            final BeanFactory parent = this;

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

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

  7.                                getInstantiationStrategy().instantiate(mbd, beanName, parent),

  8.                        getAccessControlContext());

  9.            }

  10.            else {

  11.                beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);

  12.            }

  13.            BeanWrapper bw = new BeanWrapperImpl(beanInstance);

  14.            initBeanWrapper(bw);

  15.            return bw;

  16.        }

  17.        catch (Throwable ex) {

  18.            throw new BeanCreationException(

  19.                    mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);

  20.        }

  21.    }

這個方法相比於 instantiateUsingFactoryMethod() 、 autowireConstructor() 方法實在是太簡單了,因為它沒有引數,所以不需要確認經過複雜的過來來確定建構式、構造引數,所以這裡就不過多闡述了。

對於 createBeanInstance() 而言,他就是選擇合適實體化策略來為 bean 建立實體物件,具體的策略有:Supplier 回呼方式、工廠方法初始化、建構式自動註入初始化、預設建構式註入。其中工廠方法初始化和建構式自動註入初始化兩種方式最為複雜,主要是因為建構式和構造引數的不確定性,Spring 需要花大量的精力來確定建構式和構造引數,如果確定了則好辦,直接選擇實體化策略即可。當然在實體化的時候會根據是否有需要改寫或者動態替換掉的方法,因為存在改寫或者織入的話需要建立動態代理將方法織入,這個時候就只能選擇 CGLIB 的方式來實體化,否則直接利用反射的方式即可,方便快捷。

到這裡 createBeanInstance() 的過程就已經分析完畢了,下篇介紹 doCreateBean() 方法中的第二個過程:屬性填充。

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

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

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

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

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

END


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

贊(0)

分享創造快樂

© 2024 知識星球   網站地圖