22--Spring通過有參構(gòu)造方法實(shí)例化單例bean

上一節(jié)我們分析了Spring通過默認(rèn)構(gòu)造函數(shù)實(shí)例化bean的過程,本小節(jié)分析Spring使用有參構(gòu)造函數(shù)實(shí)例化bean的過程。

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

    // 確保此時(shí)beanClass已經(jīng)被解析
    Class<?> beanClass = resolveBeanClass(mbd, beanName);

    // beanClass不為空,且beanClass的修飾符為不為public,且不允許訪問非公共構(gòu)造函數(shù)和方法,則拋出異常
    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());
    }

    // ① Spring5.0新增的實(shí)例化策略,如果設(shè)置了該策略,將會覆蓋構(gòu)造方法和工廠方法實(shí)例化策略
    Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
    if (instanceSupplier != null) {
        return obtainFromSupplier(instanceSupplier, beanName);
    }

    // ② 如果有工廠方法的話,則使用工廠方法實(shí)例化bean
    if (mbd.getFactoryMethodName() != null)  {
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }

    // ③ 當(dāng)創(chuàng)建一個(gè)相同的bean時(shí),使用之間保存的快照
    // 這里可能會有一個(gè)疑問,什么時(shí)候會創(chuàng)建相同的bean呢?
    //      ③-->① 單例模式: Spring不會緩存該模式的實(shí)例,那么對于單例模式的bean,什么時(shí)候會用到該實(shí)例化策略呢?
    //                 我們知道對于IoC容器除了可以索取bean之外,還能銷毀bean,當(dāng)我們調(diào)用xmlBeanFactory.destroyBean(myBeanName,myBeanInstance),
    //                 銷毀bean時(shí),容器是不會銷毀已經(jīng)解析的構(gòu)造函數(shù)快照的,如果再次調(diào)用xmlBeanFactory.getBean(myBeanName)時(shí),就會使用該策略了.
    //      ③-->② 原型模式: 對于該模式的理解就簡單了,IoC容器不會緩存原型模式bean的實(shí)例,當(dāng)我們第二次向容器索取同一個(gè)bean時(shí),就會使用該策略了.
    boolean resolved = false;
    boolean autowireNecessary = false;
    if (args == null) {
        synchronized (mbd.constructorArgumentLock) {
            if (mbd.resolvedConstructorOrFactoryMethod != null) {
                resolved = true;
                autowireNecessary = mbd.constructorArgumentsResolved;
            }
        }
    }
    // 如果該bean已經(jīng)被解析過
    if (resolved) {
        // 使用已經(jīng)解析過的構(gòu)造函數(shù)實(shí)例化
        if (autowireNecessary) {
            return autowireConstructor(beanName, mbd, null, null);
        }
        // 使用默認(rèn)無參構(gòu)造函數(shù)實(shí)例化
        else {
            return instantiateBean(beanName, mbd);
        }
    }

    // ④ 確定需要使用的構(gòu)造函數(shù)
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null ||
            mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
            mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
        return autowireConstructor(beanName, mbd, ctors, args);
    }

    // ⑤ 無任何的特殊處理,則使用默認(rèn)的無參構(gòu)造函數(shù)實(shí)例化bean
    return instantiateBean(beanName, mbd);
}

從代碼中可以看到,如果該bean的構(gòu)造函數(shù)已經(jīng)被解析并緩存的話,則優(yōu)先使用已經(jīng)被解析的構(gòu)造函數(shù)實(shí)例化,否則解析bean的構(gòu)造函數(shù)并實(shí)例化。

在這里我們再一次看到了Spring對緩存的應(yīng)用,而且解析構(gòu)造函數(shù)是一個(gè)比較復(fù)雜耗時(shí)的操作,所以這里需要緩存已經(jīng)解析的構(gòu)造函數(shù)。

1.測試用例

打開day01下的MyTest類:

@Test
public void test2() {
    // 指定構(gòu)造器
    System.out.println("有參構(gòu)造器");
    Dog dog2 = xmlBeanFactory.getBean("dog2", Dog.class);
    dog2.sayHello();
}
2.解析構(gòu)造函數(shù)
  • 創(chuàng)建ConstructorResolver對象
protected BeanWrapper autowireConstructor(
        String beanName,
        RootBeanDefinition mbd,
        @Nullable Constructor<?>[] ctors,
        @Nullable Object[] explicitArgs) {

    return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
  • 解析構(gòu)造函數(shù)
public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd,
            @Nullable Constructor<?>[] chosenCtors, @Nullable final Object[] explicitArgs) {

    BeanWrapperImpl bw = new BeanWrapperImpl();
    this.beanFactory.initBeanWrapper(bw);

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

    // 1、 判斷有無顯式指定參數(shù),如果有則優(yōu)先使用,如xmlBeanFactory.getBean("cat", "美美",3);
    if (explicitArgs != null) {
        argsToUse = explicitArgs;
    }
    // 2、 沒有顯式指定參數(shù),則解析配置文件中的參數(shù)
    else {
        Object[] argsToResolve = null;
        // 3、 優(yōu)先嘗試從緩存中獲取,spring對參數(shù)的解析過程是比較復(fù)雜也耗時(shí)的,所以這里先嘗試從緩存中獲取已經(jīng)解析過的構(gòu)造函數(shù)參數(shù)
        synchronized (mbd.constructorArgumentLock) {
            constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
            if (constructorToUse != null && mbd.constructorArgumentsResolved) {
                // Found a cached constructor...
                argsToUse = mbd.resolvedConstructorArguments;
                if (argsToUse == null) {
                    argsToResolve = mbd.preparedConstructorArguments;
                }
            }
        }
        // 緩存中存在,則解析構(gòu)造函數(shù)參數(shù)類型
        if (argsToResolve != null) {
            argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
        }
    }

    // 4、 緩存中不存在,則需要解析構(gòu)造函數(shù)參數(shù),以確定使用哪一個(gè)構(gòu)造函數(shù)來進(jìn)行實(shí)例化
    if (constructorToUse == null) {
        boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
        ConstructorArgumentValues resolvedValues = null;

        // 這里定義了一個(gè)變量,來記錄最小的構(gòu)造函數(shù)參數(shù)個(gè)數(shù),其作用可以參見下面解釋...
        int minNrOfArgs;
        if (explicitArgs != null) {
            minNrOfArgs = explicitArgs.length;
        }
        else {
            ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
            resolvedValues = new ConstructorArgumentValues();
            minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
        }

        // Take specified constructors, if any.
        // 5、 使用指定的構(gòu)造函數(shù)(如果有的話)。
        // 注意: 5.1、 這里說的指定構(gòu)造函數(shù),并不是我們在配置文件中指定的構(gòu)造函數(shù),而是通過解析SmartInstantiationAwareBeanPostProcessor得出的構(gòu)造函數(shù)
        //           參見AbstractAutowireCapableBeanFactory-->determineConstructorsFromBeanPostProcessors(beanClass, beanName),
        //           就是在本方法被調(diào)用之前執(zhí)行的解析操作
        //      5.2、 即便解析出來的構(gòu)造函數(shù)不為空,但是大家要注意,candidates是個(gè)數(shù)組,下一步依然還是要對candidates進(jìn)行解析,以確定使用哪一個(gè)構(gòu)造函數(shù)進(jìn)行實(shí)例化
        Constructor<?>[] candidates = chosenCtors;
        if (candidates == null) {
            Class<?> beanClass = mbd.getBeanClass();
            try {
                // 6、 如果指定的構(gòu)造函數(shù)不存在,則根據(jù)方法訪問級別,獲取該bean所有的構(gòu)造函數(shù)
                // 對于本例來分析,應(yīng)該會獲取到四個(gè)構(gòu)造函數(shù)Cat(),Cat(String name),Cat(int age),Cat(String name, int age)
                // 注意:該處獲取到的構(gòu)造函數(shù),并不是配置文件中定義的構(gòu)造函數(shù),而是bean類中的構(gòu)造函數(shù)
                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);
            }
        }

        // 7、 對構(gòu)造函數(shù)按照參數(shù)個(gè)數(shù)和參數(shù)類型進(jìn)行排序,參數(shù)最多的構(gòu)造函數(shù)會排在第一位
        AutowireUtils.sortConstructors(candidates);
        int minTypeDiffWeight = Integer.MAX_VALUE;
        Set<Constructor<?>> ambiguousConstructors = null;
        LinkedList<UnsatisfiedDependencyException> causes = null;

        // 8、 循環(huán)所有bean類中的構(gòu)造函數(shù),解析確定使用哪一個(gè)構(gòu)造函數(shù)
        for (Constructor<?> candidate : candidates) {
            Class<?>[] paramTypes = candidate.getParameterTypes();

            // 如果constructorToUse不為空,且參數(shù)個(gè)數(shù)大于當(dāng)前循環(huán)的構(gòu)造函數(shù)參數(shù)個(gè)數(shù),則直接終止循環(huán),因?yàn)榻馕龅腷ean類構(gòu)造函數(shù)已經(jīng)經(jīng)過排序
            // 問題: 進(jìn)入該if語句的條件是constructorToUse==null? 該處判斷是否多余.. ?
            // 帶著這個(gè)問題,可以接著看源碼...該處的設(shè)計(jì)還是值得參考的
            if (constructorToUse != null && argsToUse.length > paramTypes.length) {
                break;
            }

            // 如果從bean類中解析到的構(gòu)造函數(shù)個(gè)數(shù)小于從beanDefinition中解析到的構(gòu)造函數(shù)個(gè)數(shù),
            // 那么肯定不會使用該方法實(shí)例化,循環(huán)繼續(xù)
            // 簡單的理解:beanDefinition中的構(gòu)造函數(shù)和bean類中的構(gòu)造函數(shù)參數(shù)個(gè)數(shù)不相等,那么肯定不會使用該構(gòu)造函數(shù)實(shí)例化
            if (paramTypes.length < minNrOfArgs) {
                continue;
            }

            // 9、 獲取ArgumentsHolder對象,直接理解為一個(gè)參數(shù)持有者即可
            ArgumentsHolder argsHolder;
            if (resolvedValues != null) {
                try {
                    String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
                    if (paramNames == null) {
                        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;
                }
            }
            else {
                // Explicit arguments given -> arguments length must match exactly.
                if (paramTypes.length != explicitArgs.length) {
                    continue;
                }
                argsHolder = new ArgumentsHolder(explicitArgs);
            }

            // 10、 通過構(gòu)造函數(shù)參數(shù)權(quán)重對比,得出最適合使用的構(gòu)造函數(shù)
            // 先判斷是返回是在寬松模式下解析構(gòu)造函數(shù)還是在嚴(yán)格模式下解析構(gòu)造函數(shù)。(默認(rèn)是寬松模式)
            // 10.1、對于寬松模式:例如構(gòu)造函數(shù)為(String name,int age),配置文件中定義(value="美美",value="3")
            //   那么對于age來講,配置文件中的"3",可以被解析為int也可以被解析為String,
            //   這個(gè)時(shí)候就需要來判斷參數(shù)的權(quán)重,使用ConstructorResolver的靜態(tài)內(nèi)部類ArgumentsHolder分別對字符型和數(shù)字型的參數(shù)做權(quán)重判斷
            //   權(quán)重越小,則說明構(gòu)造函數(shù)越匹配
            // 10.2、對于嚴(yán)格模式:嚴(yán)格返回權(quán)重值,不會根據(jù)分別比較而返回比對值
            // 10.3、minTypeDiffWeight = Integer.MAX_VALUE;而權(quán)重比較返回結(jié)果都是在Integer.MAX_VALUE做減法,起返回最大值為Integer.MAX_VALUE
            int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
                    argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
            // Choose this constructor if it represents the closest match.
            // 如果此構(gòu)造函數(shù)表示最接近的匹配,則選擇此構(gòu)造函數(shù)
            if (typeDiffWeight < minTypeDiffWeight) {
                // 將解析到的構(gòu)造函數(shù)賦予constructorToUse,這也是我們上面問題疑問的答案所在處
                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);
            }
        }

        // 11、 異常處理
        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);
        }

        // 12、 緩存解析的構(gòu)造函數(shù)
        if (explicitArgs == null) {
            argsHolderToUse.storeCache(mbd, constructorToUse);
        }
    }

    try {
        // 13、 獲取實(shí)例化策略并執(zhí)行實(shí)例化
        final InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy();
        Object beanInstance;

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

        // 14、 返回BeanWrapper包裝類
        bw.setBeanInstance(beanInstance);
        return bw;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "Bean instantiation via constructor failed", ex);
    }
}

這個(gè)過程是相當(dāng)復(fù)雜的,逐步分析其過程:

2.1 判斷有無顯式指定參數(shù),如果有則優(yōu)先使用,如xmlBeanFactory.getBean("cat", "美美",3);
2.2 優(yōu)先嘗試從緩存中獲取,spring對參數(shù)的解析過程是比較復(fù)雜也耗時(shí)的,所以這里先嘗試從緩存中獲取已經(jīng)解析過的構(gòu)造函數(shù)參數(shù)
2.3 緩存中不存在,則需要解析構(gòu)造函數(shù)參數(shù),以確定使用哪一個(gè)構(gòu)造函數(shù)來進(jìn)行實(shí)例化

一個(gè)類可能存在多個(gè)構(gòu)造函數(shù),如Dog(String name,int age);Dog(String name);Dog(int age)等等,所以需要解析對構(gòu)造函數(shù)進(jìn)行解析,以確定使用哪一個(gè)構(gòu)造函數(shù)。

2.4 使用指定的構(gòu)造函數(shù)(如果有的話)。

這里說的指定構(gòu)造函數(shù),并不是我們在配置文件中指定的構(gòu)造函數(shù),而是通過解析SmartInstantiationAwareBeanPostProcessor得出的構(gòu)造函數(shù)。
參見AbstractAutowireCapableBeanFactory-->determineConstructorsFromBeanPostProcessors(beanClass, beanName),就是在本方法被調(diào)用之前執(zhí)行的解析操作,即便解析出來的構(gòu)造函數(shù)不為空,但是大家要注意,candidates是個(gè)數(shù)組,下一步依然還是要對candidates進(jìn)行解析,以確定使用哪一個(gè)構(gòu)造函數(shù)進(jìn)行實(shí)例化。

2.5 如果指定的構(gòu)造函數(shù)不存在,則根據(jù)方法訪問級別,獲取該bean所有的構(gòu)造函數(shù)

需要注意,該步驟獲取的是類的構(gòu)造函數(shù),而不是在配置文件中的構(gòu)造函數(shù)。

2.6 對構(gòu)造函數(shù)按照參數(shù)個(gè)數(shù)和參數(shù)類型進(jìn)行排序,參數(shù)最多的構(gòu)造函數(shù)會排在第一位
2.7 循環(huán)所有bean類中的構(gòu)造函數(shù),解析確定使用哪一個(gè)構(gòu)造函數(shù)

首先因?yàn)闃?gòu)造函數(shù)已經(jīng)按照參數(shù)的個(gè)數(shù)排序,參數(shù)個(gè)數(shù)最多的排在最前面,所以判斷如若解析出來的構(gòu)造函數(shù)個(gè)數(shù)小于BeanDefinition中的構(gòu)造函數(shù)個(gè)數(shù),那么肯定不會使用該構(gòu)造函數(shù)進(jìn)行實(shí)例化,那么循環(huán)會繼續(xù)。
其次將解析到的構(gòu)造函數(shù)封裝至ArgumentsHolder對象。
最后通過構(gòu)造函數(shù)參數(shù)權(quán)重對比,得出最適合使用的構(gòu)造函數(shù)。

2.8 處理異常,緩存解析過的構(gòu)造函數(shù)。
2.9 獲取實(shí)例化策略并執(zhí)行實(shí)例化

同樣這里也會有反射或CGLIB實(shí)例化bean,具體的細(xì)節(jié),上一節(jié)已經(jīng)分析過。

2.10 返回BeanWrapper包裝類
3.總結(jié)

本節(jié)的重點(diǎn)和難點(diǎn)在于對構(gòu)造函數(shù)的確定,大家可以多定義一些有參構(gòu)造函數(shù),多通過調(diào)試來加深理解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.1 spring IoC容器和beans的簡介 Spring 框架的最核心基礎(chǔ)的功能是IoC(控制反轉(zhuǎn))容器,...
    simoscode閱讀 6,851評論 2 22
  • 1.1 Spring IoC容器和bean簡介 本章介紹了Spring Framework實(shí)現(xiàn)的控制反轉(zhuǎn)(IoC)...
    起名真是難閱讀 2,674評論 0 8
  • 上一節(jié)分析了Spring實(shí)例化單例bean的準(zhǔn)備工作,而且已經(jīng)接觸到了真正創(chuàng)建bean的方法doCreateBea...
    閑來也無事閱讀 2,361評論 0 3
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,657評論 19 139
  • 她走過來,對我微微一笑,只是那種很平常的微微一笑。她是我的傷口嗎?是的,永久的傷口。 柳拾意《尋云錄》
    柳拾意閱讀 265評論 0 0

友情鏈接更多精彩內(nèi)容