Spring IoC bean 的創建

前言

本系列全部基於 Spring 5.2.2.BUILD-SNAPSHOT 版本。因為 Spring 整個體系太過於龐大,所以只會進行關鍵部分的源碼解析。

本篇文章主要介紹 Spring IoC 容器是怎麼創建 bean 的實例。

正文

在上一篇Spring IoC bean 的加載中有這麼一段代碼:

if (mbd.isSingleton()) {
    // 創建和註冊單例 bean
    sharedInstance = getSingleton(beanName, () -> {
        try {
            // 創建 bean 實例
            return createBean(beanName, mbd, args);
        }
       	// 省略異常處理...
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

如果 bean 的作用域是單例,會調用 getSingleton() 方法並傳入 beanNameObjectFacoty作為參數;而 getSingleton() 方法會調用 ObjectFactorygetObject() 方法也就是上面代碼中的 createBean() 方法,返回 bean

這裏的 ObjectFactorybean 的延遲依賴查找接口,定義如下:

@FunctionalInterface
public interface ObjectFactory<T> {

    T getObject() throws BeansException;

}

只有在調用 getObject() 方法時才會真正去獲取 bean。下面我們正式開始分析 createBean() 方法。

AbstractAutowireCapableBeanFactory#createBean

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {

    RootBeanDefinition mbdToUse = mbd;

    // 將String類型的class字符串,轉換為Class對象,例如在XML中配置的class屬性
    Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
    if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
        mbdToUse = new RootBeanDefinition(mbd);
        mbdToUse.setBeanClass(resolvedClass);
    }

    try {
        // 進行定義的方法覆蓋
        mbdToUse.prepareMethodOverrides();
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex);
    }

    try {
        // 如果bean的實例化前回調方法返回非null,直接返回實例,跳過後面步驟。見下文詳解
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    }
    catch (Throwable ex) {
        throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex);
    }

    try {
        // 真正去創建bean的方法
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isTraceEnabled()) {
            logger.trace("Finished creating instance of bean '" + beanName + "'");
        }
        // 返回bean的實例
        return beanInstance;
    }
    // 省略異常處理...
}

bean 實例化前置處理

我們先看一下 InstantiationAwareBeanPostProcessor 接口的定義:

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

    /**
     * Bean 實例化前調用,返回非 {@code null} 回調過後面流程
     * 返回 {@code null} 則進行 IoC 容器對 Bean 的實例化
     */
    @Nullable
    default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    /**
     * Bean 實例化之後,屬性填充之前調用,返回 {@code true} 則進行默認的屬性填充步驟,
     * 返回 {@code false} 會跳過屬性填充階段,同樣也會跳過初始化階段的生命周期方法的回調。
     */
    default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

    /**
     * Bean 實例化后屬性賦值前調用,PropertyValues 是已經封裝好的設置的屬性值,返回 {@code null} 繼續
     */
    @Nullable
    default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        return null;
    }

    /**
     * 5.1 版本后已經被上面 postProcessProperties 方法所替代,功能與上面方法一樣
     */
    @Deprecated
    @Nullable
    default PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        return pvs;
    }

}

上面接口繼承於 BeanPostProcessorBeanPostProcessor 中定義了 bean 的初始化階段生命周期回調方法,會在後續介紹)提供了三個擴展點,如下:

  • bean 實例化前
  • bean 實例化后
  • bean 屬性賦值前

這也是 bean 實例化階段的生命周期回調方法。

AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    Object bean = null;
    // 判斷bean在實例化之前是否已經解析過
    if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
        // 如果bean是合成的 && 有實現 InstantiationAwareBeanPostProcessor 接口
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            // 解析bean的類型
            Class<?> targetType = determineTargetType(beanName, mbd);
            if (targetType != null) {
                // 執行bean的實例化前回調
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                // 如果實例化前生命周期回調方法返回的不是null
                if (bean != null) {
                    // 執行bean的實例化后回調,因為會跳過後續步驟,所以只能在此處調用了
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        // 如果bean不為空,則將beforeInstantiationResolved賦值為true,代表在實例化之前已經解析
        mbd.beforeInstantiationResolved = (bean != null);
    }
    return bean;
}

創建 bean

AbstractAutowireCapableBeanFactory#doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {

    // 實例化 bean
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        // 如果bean的作用域是singleton,則需要移除未完成的FactoryBean實例的緩存
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        // 通過構造函數反射創建bean的實例,但是屬性並未賦值,見下文詳解
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    // 獲取bean的實例
    final Object bean = instanceWrapper.getWrappedInstance(); 
    // 獲取bean的類型
    Class<?> beanType = instanceWrapper.getWrappedClass(); 
    if (beanType != NullBean.class) {
        mbd.resolvedTargetType = beanType;
    }

    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                // BeanDefinition 合併后的回調,見下文詳解
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            } 
            // 省略異常處理...
            mbd.postProcessed = true;
        }
    }

    // bean的作用域是單例 && 允許循環引用 && 當前bean正在創建中
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
    // 如果允許bean提前曝光
    if (earlySingletonExposure) {
        // 將beanName和ObjectFactory形成的key-value對放入singletonFactories緩存中
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    Object exposedObject = bean;
    try {
        // 給 bean 的屬性賦值
        populateBean(beanName, mbd, instanceWrapper);
        // 初始化 bean
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    } 
    // 省略異常處理...
    
    // 如果允許單例bean提前暴露
    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        // 只有在檢測到循環依賴的情況下才不為空
        if (earlySingletonReference != null) {
            // 如果exposedObject沒有在初始化方法中被改變,也就是沒有被增強
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                // 檢測依賴
                for (String dependentBean : dependentBeans) { 
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
                if (!actualDependentBeans.isEmpty()) {
                    throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                }
            }
        }
    }

    try {
        // 用於註冊銷毀bean
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    } catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }
    // 返回bean實例
    return exposedObject;
}

創建 bean 的實例

AbstractAutowireCapableBeanFactory#createBeanInstance

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // 解析 bean 的類型
    Class<?> beanClass = resolveBeanClass(mbd, beanName);
    // 判斷beanClass是否是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());
    }
    // Spring 5新添加的,如果存在Supplier回調,則使用給定的回調方法初始化策略。
    // 可以使RootBeanDefinition#setInstanceSupplier()設置
    Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
    if (instanceSupplier != null) {
        return obtainFromSupplier(instanceSupplier, beanName);
    }
    // 如果設置工廠方法則使用給定的方法創建bean實例,這裏分為靜態工廠和實例化工廠
    if (mbd.getFactoryMethodName() != null) {
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }

    // 快捷方式創建相同的bean
    // resolved: 構造函數或工廠方法是否已經解析過
    boolean resolved = false;
    // autowireNecessary: 是否需要自動注入 (即是否需要解析構造函數)
    boolean autowireNecessary = false;
    if (args == null) {
        synchronized (mbd.constructorArgumentLock) {
            // 如果resolvedConstructorOrFactoryMethod不為空,代表構造函數或工廠方法已經解析過
            if (mbd.resolvedConstructorOrFactoryMethod != null) {
                resolved = true;
                // 根據constructorArgumentsResolved判斷是否需要自動注入
                autowireNecessary = mbd.constructorArgumentsResolved;
            }
        }
    }
    if (resolved) {
        if (autowireNecessary) {
            // 如果構造函數或工廠方法已經解析過並且需要自動注入,則執行構造器自動注入,見下文詳解
            return autowireConstructor(beanName, mbd, null, null);
        }
        else {
            // 否則使用默認構造函數進行bean實例化,見下文詳解
            return instantiateBean(beanName, mbd);
        }
    }

    // 調用SmartInstantiationAwareBeanPostProcessor的determineCandidateConstructors()方法
    // 拿到 bean 的候選構造函數
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    // 候選構造函數不為空 || 構造函數依賴注入 || 定義了構造函數的參數值 || args不為空,則執行構造器自動注入
    if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
        mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
        return autowireConstructor(beanName, mbd, ctors, args);
    }

    // 如果有首選的構造函數,使用該構造函數去創建bean實例
    ctors = mbd.getPreferredConstructors();
    if (ctors != null) {
        return autowireConstructor(beanName, mbd, ctors, null);
    }

    // 沒有特殊處理,使用默認無參構造器實例化bean
    return instantiateBean(beanName, mbd);
}

上面方法主要判斷是使用構造函數自動注入,還是使用默認構造函數構造。總結起來以下幾種情況會使用構造函數自動注入:

  • 已經緩存過構造函數並且構造函數的參數已經解析過。
  • 候選的構造函數不為空,這裏的候選構造函數是通過實現 SmartInstantiationAwareBeanPostProcessor 接口中的 determineCandidateConstructors() 方法
  • 自動注入模式為構造函數自動注入
  • BeanDefinition 定義了構造函數參數,如 XML 中的 <constructor-arg index="0" value="1"/>
  • 在調用 getBean() 方法時显示指定了 args 參數

上面方法中還有一個判斷是否有緩存的過程,是因為一個 bean 對應的類中可能會有多個構造函數,而每個構造函數的參數不同,Spring 在根據參數及類型去判斷最終會使用哪個構造函數進行實例化。但是,判斷的過程是個比較消耗性能的步驟,所以採用緩存機制,如果已經解析過則不需要重複解析而是直接從 RootBeanDefinition 中的屬性 resolvedConstructorOrFactoryMethod 緩存的值去取,否則需要再次解析,並將解析的結果添加至 RootBeanDefinition 中的屬性 resolvedConstructorOrFactoryMethod 中。

這裏簡單介紹一下 SmartInstantiationAwareBeanPostProcessor 這個接口,它繼承於 InstantiationAwareBeanPostProcessor,如下:

public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {

    /**
     * 預測 bean 的類型
     */
    @Nullable
    default Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    /**
     * 選擇合適的構造器,比如目標對象有多個構造器,在這裏可以進行一些定製化,選擇合適的構造器
     */
    @Nullable
    default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    /**
     * 獲得提前暴露的 bean 引用,主要用於解決循環引用的問題
     * 只有單例對象才會調用此方法
     */
    default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        return bean;
    }

}

其實我們熟知的 @Autowired 註解標註在構造函數上實現自動注入,也是重寫了該接口的 determineCandidateConstructors() 方法實現的。

默認無參構造器實例化 bean

AbstractAutowireCapableBeanFactory#instantiateBean

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
    try {
        Object beanInstance;
        final BeanFactory parent = this;
        // 使用指定的策略去實力化bean
        beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
        // 將實例化后的bean封裝成BeanWrapper后返回
        BeanWrapper bw = new BeanWrapperImpl(beanInstance);
        initBeanWrapper(bw);
        return bw;
    }
    // 省略異常處理...
}

// SimpleInstantiationStrategy.java
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    // 如果有需要覆蓋或者動態替換的方法則當然需要使用CGLIB進行動態代理,因為可以在創建代理的同時將方法織入類中
    // 但是如果沒有需要動態改變的方法,為了方便直接用反射就可以了
    if (!bd.hasMethodOverrides()) {
        Constructor<?> constructorToUse;
        synchronized (bd.constructorArgumentLock) {
            // 獲取緩存的構造方法或工廠方法
            constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
            // 緩存為空
            if (constructorToUse == null) {
                final Class<?> clazz = bd.getBeanClass();
                // 如果clazz是接口,拋出異常
                if (clazz.isInterface()) {
                    throw new BeanInstantiationException(clazz, "Specified class is an interface");
                }
                try {
                    // 獲取默認的無參構造函數
                    constructorToUse = clazz.getDeclaredConstructor();
                    // 設置緩存
                    bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                }
                catch (Throwable ex) {
                    throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                }
            }
        }
        // 這裏就是用指定的無參構造器去實例化該bean,不做具體分析了
        return BeanUtils.instantiateClass(constructorToUse);
    }
    else {
        // 用CGLIB生成子類動態織入重寫的方法
        return instantiateWithMethodInjection(bd, beanName, owner);
    }
}

尋找合適的構造器實例化 bean

ConstructorResolver#autowireConstructor

protected BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
	// 尋找適合的構造器,進行實例化
    return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}

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

    BeanWrapperImpl bw = new BeanWrapperImpl();
    this.beanFactory.initBeanWrapper(bw);
    // 最終實例化的構造函數
    Constructor<?> constructorToUse = null;
    // 最終用於實例化的參數Holder
    ArgumentsHolder argsHolderToUse = null;
    // 最終用於實例化的構造函數參數
    Object[] argsToUse = null;
    // 如果explicitArgs不為空,則使用explicitArgs當做構造器函數參數
    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) {
                    // 如果已經緩存了構造函數或工廠方法,
                    // 那麼resolvedConstructorArguments和preparedConstructorArguments必定有一個緩存了構造函數參數
                    argsToResolve = mbd.preparedConstructorArguments;
                }
            }
        }
        if (argsToResolve != null) {
            // 如果argsToResolve不為空,則對構造函數參數進行解析,也就是會進行類型轉換之類的操作
            // 例如 A(int,int),把配置中的 ("1","1") 轉換為 (1,1)
            argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
        }
    }
    // 如果沒有緩存構造函數或者其參數
    if (constructorToUse == null || argsToUse == null) {
        Constructor<?>[] candidates = chosenCtors;
        if (candidates == null) {
            Class<?> beanClass = mbd.getBeanClass();
            try {
                // 如果允許訪問非public的構造函數和方法(該值默認為 true),就獲取所有構造函數,否則只獲取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);
            }
        }
        // 如果只有一個構造函數 && getBean()沒有显示指定args && 沒有定義構造函數的參數值
        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;
                }
                // 通過無參構造函數創建bean的實例,然後直接返回
                bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
                return bw;
            }
        }

        // 如果候選構造函數不為空 || 構造函數自動注入模式
        boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
        ConstructorArgumentValues resolvedValues = null;

        int minNrOfArgs;
        // getBean()显示指定了參數,獲取參數長度
        if (explicitArgs != null) {
            minNrOfArgs = explicitArgs.length;
        }
        else {
            // 獲取定義的構造函數參數
            ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
            resolvedValues = new ConstructorArgumentValues();
            // 解析構造函數參數並賦值到resolvedValues,返回參數個數。見下文詳解
            minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
        }
        // 這裏對構造函數進行排序,規則是首先是public構造函數且參數個數從多到少,然後是非public構造函數且參數個數有多到少
        AutowireUtils.sortConstructors(candidates);
        // 最小匹配權重,權重越小,越接近我們要找的目標構造函數
        int minTypeDiffWeight = Integer.MAX_VALUE;
        Set<Constructor<?>> ambiguousConstructors = null;
        LinkedList<UnsatisfiedDependencyException> causes = null;
        // 遍歷構造函數,找出符合的構造函數
        for (Constructor<?> candidate : candidates) {
            // 獲取參數數量
            int parameterCount = candidate.getParameterCount();
            // 如果已經找到滿足的構造函數 && 目標構造函數參數個數大於當前遍歷的構造函數參數個數則終止
            // 因為構造函數已經是排過序的,後面不會再有更適合的了
            if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
                break;
            }
            // 如果目標的構造函數參數個數小於我們需要的,直接跳過
            if (parameterCount < minNrOfArgs) {
                continue;
            }

            ArgumentsHolder argsHolder;
            // 獲取到構造函數的參數類型
            Class<?>[] paramTypes = candidate.getParameterTypes();
            if (resolvedValues != null) {
                try {
                    // 評估參數名稱,就是判斷構造函數上是否標註了@ConstructorProperties註解,如果標註了,直接取其中定義的參數名稱
                    String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
                    // 沒有標註@ConstructorProperties註解,使用參數名稱解析器,獲取參數名稱
                    if (paramNames == null) {
                        ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
                        if (pnd != null) {
                            paramNames = pnd.getParameterNames(candidate);
                        }
                    }
                    // 創建一個參數數組以調用構造函數或工廠方法,見下文詳解
                    // 主要是通過參數類型和參數名解析構造函數或工廠方法所需的參數(如果參數是其他bean,則會解析依賴的bean)
                    argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
                }
                catch (UnsatisfiedDependencyException ex) {
                    // Swallow and try next constructor.
                    if (causes == null) {
                        causes = new LinkedList<>();
                    }
                    causes.add(ex);
                    continue;
                }
            }
            // resolvedValues為空, explicitArgs不為空,即显示指定了getBean()的args參數
            else {
                // 如果當前構造函數參數個數不等的explicitArgs的長度,直接跳過該構造函數
                if (parameterCount != explicitArgs.length) {
                    continue;
                }
                // 把explicitArgs封裝進ArgumentsHolder
                argsHolder = new ArgumentsHolder(explicitArgs);
            }
            // 根據mbd的解析構造函數模式(true: 寬鬆模式,false:嚴格模式)
            // 將argsHolder的參數和paramTypes進行比較,計算paramTypes的類型差異權重值
            int typeDiffWeight = (mbd.isLenientConstructorResolution() ?argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
            // 差異值越小代表構造函數越匹配,則選擇此構造函數
            if (typeDiffWeight < minTypeDiffWeight) {
                constructorToUse = candidate;
                argsHolderToUse = argsHolder;
                argsToUse = argsHolder.arguments;
                minTypeDiffWeight = typeDiffWeight;
                // 如果出現權重值更小的候選者,則將ambiguousConstructors清空,允許之前存在權重值相同的候選者
                ambiguousConstructors = null;
            }
            // 兩個候選者權重值相同,並且是當前遍歷過權重值最小的
            else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
                // 將兩個候選者添加到ambiguousConstructors
                if (ambiguousConstructors == null) {
                    ambiguousConstructors = new LinkedHashSet<>();
                    ambiguousConstructors.add(constructorToUse);
                }
                ambiguousConstructors.add(candidate);
            }
        }
        // 沒有找到匹配的構造函數,拋出異常
        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);
        }
        // getBean()方法沒有指定args參數 && 構造函數參數不為空
        if (explicitArgs == null && argsHolderToUse != null) {
            // 緩存解析過後的構造函數和參數
            argsHolderToUse.storeCache(mbd, constructorToUse);
        }
    }

    Assert.state(argsToUse != null, "Unresolved constructor arguments");
    // 利用反射創建bean實例
    bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
    return bw;
}

上面方法的功能主要如下:

  1. 構造函數參數的確定
    • 如果 explicitArgs 參數不為空,那就可以直接確定參數。因為 explicitArgs 參數是在調用 getBean() 時手動指定的,這個主要用於靜態工廠方法的調用。
    • 緩存中不為空,那麼可以直接拿過來使用。
    • BeanDefinition 中讀取,我們所定義的 bean 都會生成一個 BeanDefinition ,其中記錄了定義了構造函數參數通過 getConstructorArgumentValues() 獲取。
  2. 構造函數的確定。經過第一步已經確定構造函數的參數,接下來就是用參數個數在所有的構造函數中鎖定對應的構造函數。匹配之前會對構造函數進行排序,首先是 public 構造函數且參數個數從多到少,然後是非public 構造函數且參數個數有多到少。這樣可以迅速判斷排在後面的構造函數參數個數是否符合條件。
  3. 根據對應的構造函數轉換對應的參數類型。
  4. 根據實例化策略以及得到的構造函數和構造函數參數實例化 bean

解析構造函數參數

ConstructorResolver#resolveConstructorArguments
private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {
    // 獲取自定義類型轉換器
    TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
    TypeConverter converter = (customConverter != null ? customConverter : bw); 
    // 如果沒有自定義的轉換器就用bw
    BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
    // minNrOfArgs初始化為indexedArgumentValues+genericArgumentValues的個數總和
    int minNrOfArgs = cargs.getArgumentCount();
    // 遍歷IndexArgumentValues,這裏的IndexArgumentValues就帶下標的,如:<constructor-arg index="0" value="1"/>
    for (Map.Entry<Integer, ConstructorArgumentValues.ValueHolder> entry : cargs.getIndexedArgumentValues().entrySet()) {
        int index = entry.getKey();
        if (index < 0) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid constructor argument index: " + index);
        } 
        // 如果index大於minNrOfArgs,修改minNrOfArgs值
        if (index > minNrOfArgs) {
            // 因為index是構造函數下標值,所以總數這邊要加1
            minNrOfArgs = index + 1; 
        }
        ConstructorArgumentValues.ValueHolder valueHolder = entry.getValue();
        // 如果參數類型已經轉換過,直接添加進resolvedValues
        if (valueHolder.isConverted()) { 
            resolvedValues.addIndexedArgumentValue(index, valueHolder);
        }
        // 參數類型沒有轉換過,進行轉換
        else { 
            Object resolvedValue =
                valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
            // 使用轉換過的參數值構建ValueHolder
            ConstructorArgumentValues.ValueHolder resolvedValueHolder = 
						new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName());
            resolvedValueHolder.setSource(valueHolder); 
            // 添加進resolvedValues
            resolvedValues.addIndexedArgumentValue(index, resolvedValueHolder);
        }
    }
    // 遍歷GenericArgumentValues並進行類型轉換和上面一樣,這裏的GenericArgumentValues就是沒有指定下標的
    // 如:<constructor-arg value="1"/>
    for (ConstructorArgumentValues.ValueHolder valueHolder : cargs.getGenericArgumentValues()) {
        if (valueHolder.isConverted()) {
            resolvedValues.addGenericArgumentValue(valueHolder);
        }
        else {
            Object resolvedValue =
                valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
            ConstructorArgumentValues.ValueHolder resolvedValueHolder = new ConstructorArgumentValues.ValueHolder(
                resolvedValue, valueHolder.getType(), valueHolder.getName());
            resolvedValueHolder.setSource(valueHolder);
            resolvedValues.addGenericArgumentValue(resolvedValueHolder);
        }
    }
    // 返回參數個數
    return minNrOfArgs;
}

上面方法主要將 indexedArgumentValuesgenericArgumentValues 屬性中的值通過調用 resolveValueIfNecessary() 方法進行解析;resolveValueIfNecessary() 方法主要解析參數的類型,比如 ref 屬性引用的 beanName 會通過 getBean() 返回實例。

創建參數數組

ConstructorResolver#createArgumentArray
private ArgumentsHolder createArgumentArray(String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable, boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {
    // 獲取類型轉換器
    TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
    TypeConverter converter = (customConverter != null ? customConverter : bw);
    // 構建ArgumentsHolder
    ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
    Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
    Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
    // 遍歷參數類型數組
    for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
        // 獲取參數類型和名稱
        Class<?> paramType = paramTypes[paramIndex]; 
        String paramName = (paramNames != null ? paramNames[paramIndex] : "");
        ConstructorArgumentValues.ValueHolder valueHolder = null;
        if (resolvedValues != null) {
            // 根據參數的下標、類型、名稱查詢是否有匹配的
            valueHolder = resolvedValues.getArgumentValue(paramIndex, paramType, paramName, usedValueHolders);
            // 沒有匹配的 && 不是自動裝配。嘗試下一個通用的無類型參數值作為降級方法
            // 它可以在類型轉換后匹配 (例如,String -> int)
            if (valueHolder == null && (!autowiring || paramTypes.length == resolvedValues.getArgumentCount())) {
                valueHolder = resolvedValues.getGenericArgumentValue(null, null, usedValueHolders);
            }
        }
        // 找到了匹配的valueHolder
        if (valueHolder != null) {
            // 添加進usedValueHolders
            usedValueHolders.add(valueHolder);
            Object originalValue = valueHolder.getValue();
            Object convertedValue;
            // 類型已經轉換過
            if (valueHolder.isConverted()) {
                // 獲取已經轉換過的值,作為args在paramIndex的預備參數
                convertedValue = valueHolder.getConvertedValue();
                args.preparedArguments[paramIndex] = convertedValue;
            }
            // 類型沒有轉換過
            else {
                // 將構造方法和參數下標封裝成MethodParameter(MethodParameter是封裝方法和參數索引的工具類)
                MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
                try {
                    // 將原始值轉換為paramType類型的值,無法轉換時拋出異常
                    convertedValue = converter.convertIfNecessary(originalValue, paramType, methodParam);
                }
                catch (TypeMismatchException ex) {
                    throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), "Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(valueHolder.getValue()) + "] to required type [" + paramType.getName() + "]: " + ex.getMessage());
                }
                Object sourceHolder = valueHolder.getSource();
                if (sourceHolder instanceof ConstructorArgumentValues.ValueHolder) {
                    Object sourceValue = ((ConstructorArgumentValues.ValueHolder) sourceHolder).getValue();
                    // 標記args需要解析
                    args.resolveNecessary = true;
                    // 將sourceValue作為args在paramIndex位置的預備參數
                    args.preparedArguments[paramIndex] = sourceValue;
                }
            }
            // 將convertedValue作為args在paramIndex位置的參數
            args.arguments[paramIndex] = convertedValue;
            //  將originalValue作為args在paramIndex位置的原始參數
            args.rawArguments[paramIndex] = originalValue;
        }
        // 沒有找到匹配的valueHolder
        else {
            // 將構造方法和參數下標封裝成MethodParameter
            MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
            // 找不到明確的匹配,並且不是自動注入,拋出異常
            if (!autowiring) {
                throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), "Ambiguous argument values for parameter of type [" + paramType.getName() +
                    "] - did you specify the correct bean references as arguments?");
            }
            try {
                // 如果是自動注入,用resolveAutowiredArgument()解析參數,見下文詳解
                // 構造函數自動注入中的參數bean就是在這邊處理
                Object autowiredArgument = resolveAutowiredArgument(
                    methodParam, beanName, autowiredBeanNames, converter, fallback);
                // 將通過自動裝配解析出來的參數賦值給args
                args.rawArguments[paramIndex] = autowiredArgument;
                args.arguments[paramIndex] = autowiredArgument;
                args.preparedArguments[paramIndex] = autowiredArgumentMarker;
                args.resolveNecessary = true;
            }
            catch (BeansException ex) {
                throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex);
            }
        }
    }
    // 如果依賴了其他的bean,則註冊依賴關係(這邊的autowiredBeanNames,就是所有依賴的beanName)
    for (String autowiredBeanName : autowiredBeanNames) {
        this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
    }
	// 返回解析后的參數值
    return args;
}

上面方法判斷構造函數如果有匹配的參數會轉換成對應類型,如果沒有匹配的參數,多半是構造函數自動注入,通過 resolveAutowiredArgument() 去查找 bean 並返回實例。

ConstructorResolver#resolveAutowiredArgument
protected Object resolveAutowiredArgument(MethodParameter param, String beanName, @Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
    // 獲取參數的類型
    Class<?> paramType = param.getParameterType();
    // 如果參數類型是InjectionPoint
    if (InjectionPoint.class.isAssignableFrom(paramType)) {
        // 拿到當前的InjectionPoint(存儲了當前正在解析依賴的方法參數信息,DependencyDescriptor)
        InjectionPoint injectionPoint = currentInjectionPoint.get();
        if (injectionPoint == null) {
            // 當前injectionPoint為空,則拋出異常:目前沒有可用的InjectionPoint
            throw new IllegalStateException("No current InjectionPoint available for " + param);
        }
        // 當前injectionPoint不為空,直接返回
        return injectionPoint;
    }
    try {
        // 解析指定依賴,DependencyDescriptor:
        // 將MethodParameter的方法參數索引信息封裝成DependencyDescriptor,見下文詳解
        return this.beanFactory.resolveDependency(
					new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
    }
    // 忽略異常處理...
}

上面方法中的 resolveDependency() 方法就是解決依賴注入的關鍵所在,在分析這個方法之前我們先簡單看一下 DependencyDescriptor 類。

public class DependencyDescriptor extends InjectionPoint implements Serializable {

    // 包裝依賴(屬性或者方法的某個參數)所在的聲明類
    private final Class<?> declaringClass;

    // 如果所包裝依賴是方法的某個參數,則這裏記錄該方法的名稱
    @Nullable
    private String methodName;

    // 如果所包裝的是方法的某個參數,則這裏記錄該參數的類型
    @Nullable
    private Class<?>[] parameterTypes;

    // 如果所包裝的是方法的某個參數,則這裏記錄該參數在該函數參數列表中的索引
    private int parameterIndex;

    // 如果所包裝的是屬性,則這裏記錄該屬性的名稱
    @Nullable
    private String fieldName;

    // 標識所包裝依賴是否必要依賴
    private final boolean required;

    // 標識所包裝依賴是否需要飢餓加載
    private final boolean eager;

    // 標識所包裝依賴的嵌套級別
    private int nestingLevel = 1;

    // 標識所包裝依賴的包含者類,通常和聲明類是同一個
    @Nullable
    private Class<?> containingClass;
    
    // 省略其他代碼...
    
}

這個類就是依賴描述符,存儲了需要注入 bean 的類型、構造器參數的下標(構造器注入該值不為空)、是否必需、字段名稱(字段注入該值不為空)、方法名稱(set 方法注入該值不為空)等。

依賴解決

DefaultListableBeanFactory#resolveDependency
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    // Optional類型的處理,說明Spring也可以注入Optional類型的參數
    if (Optional.class == descriptor.getDependencyType()) {
        return createOptionalDependency(descriptor, requestingBeanName);
    }
    // ObjectFactory或ObjectProvider類型的處理
    else if (ObjectFactory.class == descriptor.getDependencyType() ||
             ObjectProvider.class == descriptor.getDependencyType()) {
        return new DependencyObjectProvider(descriptor, requestingBeanName);
    }
    // javax.inject.Provider類型的處理
    else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
        return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
    }
    else {
        // 獲取延遲解析代理
        Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);
        if (result == null) {
            // 解析依賴,返回的result為最終需要注入的bean實例,見下文詳解
            result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
        }
        return result;
    }
}
DefaultListableBeanFactory#doResolveDependency
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    try {
        // 獲取需要注入bean的快捷方式,不為空直接返回
        Object shortcut = descriptor.resolveShortcut(this);
        if (shortcut != null) {
            return shortcut;
        }
        // 獲取需要注入bean的類型
        Class<?> type = descriptor.getDependencyType();
        // 用於支持Spring中新增的註解@Value(確定給定的依賴項是否聲明@Value註解,如果有則拿到值)
        Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
        if (value != null) {
            if (value instanceof String) {
                String strVal = resolveEmbeddedValue((String) value);
                BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                                     getMergedBeanDefinition(beanName) : null);
                value = evaluateBeanDefinitionString(strVal, bd);
            }
            TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
            try {
                return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
            }
            catch (UnsupportedOperationException ex) {
                return (descriptor.getField() != null ?
                        converter.convertIfNecessary(value, type, descriptor.getField()) :converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
            }
        }
        // 解析MultipleBean,例如 Array,Collection,Map
        Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
        if (multipleBeans != null) {
            return multipleBeans;
        }
        // 根據類型找到匹配的bean
        // matchingBeans(key: beanName value: 如果bean已經緩存了實例(例如單例bean會緩存其實例),
        // 就是bean的實例,否則就是對應的class對象)
        Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
        if (matchingBeans.isEmpty()) {
            // 沒有找到匹配的bean,判斷是不是必需的,不是直接返回null,否則拋出異常
            if (isRequired(descriptor)) {
                raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
            }
            return null;
        }

        String autowiredBeanName;
        Object instanceCandidate;
        // 如果有多個匹配的候選者
        if (matchingBeans.size() > 1) {
            // 判斷最佳的候選者,也就是尋找最匹配的beanName
            autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
            if (autowiredBeanName == null) {
                if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                    return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
                }
                else {
                    return null;
                }
            }
            // 拿到autowiredBeanName對應的value(bean實例或bean實例類型)
            instanceCandidate = matchingBeans.get(autowiredBeanName);
        }
        else {
            // 只找到一個符合的bean
            Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
            autowiredBeanName = entry.getKey();
            instanceCandidate = entry.getValue();
        }

        if (autowiredBeanNames != null) {
            // 將依賴的beanName添加到autowiredBeanNames中
            autowiredBeanNames.add(autowiredBeanName);
        }
        // 如果需要注入的bean沒有緩存實例,那麼instanceCandidate是一個Class對象,再根據getBean()去獲取對應的實例
        if (instanceCandidate instanceof Class) {
            instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
        }
        Object result = instanceCandidate;
        if (result instanceof NullBean) {
            if (isRequired(descriptor)) {
                raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
            }
            result = null;
        }
        if (!ClassUtils.isAssignableValue(type, result)) {
            throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
        }
        // 返回最終需要注入的bean實例
        return result;
    }
    finally {
        ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
    }
}

上面方法才是真正去獲取需要注入的 bean,大概分為以下幾個步驟:

  1. 查看是否有快捷方式獲取注入 bean 是否為空,不為空直接返回。這裏的快捷方式是通過繼承 DependencyDescriptor 並重寫 resolveShortcut() 來實現。

  2. 如果參數使用 @Value 註解修飾了,如果獲取到值直接返回。

  3. 解析 MultipleBean,這裏的 MultipleBean 一般是 ArrayCollectionMap 這種,不為空直接返回。

  4. 根據類型找到所有匹配的 beanmatchingBeanskeybeanNamevalue 的值有兩種情況,如果bean已經緩存了實例(例如單例bean會緩存其實例),就是bean的實例,否則就是對應的class對象)。

  5. matchingBeans 為空,判斷需要注入的 bean 是否是必須的,如果是拋出異常,否則返回 null

  6. matchingBeans 長度大於1,代表有多個候選者;選擇最佳的候選者,規則是:

    1. 首先查找 primary 屬性為 true 的。
    2. 查找優先級最高的,實現 PriorityOrdered 接口或者標註 @Priority 註解的。
    3. 查找名稱匹配的。
  7. 只有一個候選者,直接使用。

  8. 如果需要注入的 bean 沒有緩存實例,那麼 instanceCandidate是一個 Class 對象,再根據 getBean() 方法去獲取對應的實例。

  9. 最終返回需要注入的 bean 實例。

BeanDefinition 合併后處理

AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof MergedBeanDefinitionPostProcessor) {
            MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
            bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
        }
    }
}

上面代碼很簡單,無非就是拿到所有註冊的 BeanPostProcessor ,然後遍歷判斷是否是 MeragedBeanDefinitionPostProcessor 類型,是的話進行 BeanDefinition 合併后的方法回調,在這個回調方法內你可以對指定 beanBeanDefinition 做一些修改。

下面我們簡單看一下 MergedBeanDefinitionPostProcessor 接口中的方法:

public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {

    /**
     * 對指定bean的BeanDefinition合併后的處理方法回調
     */
    void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);

    /**
     * 通知已重新設置指定beanName的BeanDefinition,如果實現該方法應該清除受影響的bean的所有元數據
     * @since 5.1
     */
    default void resetBeanDefinition(String beanName) {
        
    }

}

總結

本文主要介紹了創建 bean 實例的流程,我們可以重新梳理一下思路:

  1. 進行 bean 的實例化前方法回調,如果返回非空,跳過後面步驟
  2. 創建 bean 的實例,如果是構造函數注入會選擇最適合的構造函數進行參數自動注入,否則調用默認的無參構造進行實例化 bean

由於 doCreateBean() 方法中操作太多,這裡會分為幾篇文章,一一分析各個階段。

最後,我模仿 Spring 寫了一個精簡版,代碼會持續更新。地址:https://github.com/leisurexi/tiny-spring。

參考

  • 《Spring 源碼深度解析》—— 郝佳
  • https://blog.csdn.net/andy_zhang2007/article/details/88135669
  • https://fangjian0423.github.io/2017/06/20/spring-bean-post-processor/
  • https://blog.csdn.net/v123411739/article/details/87994934

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?