Spring源碼系列(二)--bean組件的源碼分析


簡介

在上一篇博客(Spring源碼系列(一)--詳細介紹bean組件)中,我們討論了 spring-bean 是什么?用來解決什么問題?如何使用 spring-bean?等等問題,算是從使用者的角度對 spring-bean 有了一定了解。這篇博客我們將開始分析 spring-bean 的源碼,大致的思路如下:

  1. spring-bean 是如何設計的
  2. 開始看源碼--從哪里開始
  3. bean 沖突的處理
  4. 先看看是否需要創建
  5. 開始創建 bean
  6. bean 的實例化
  7. bean 的屬性裝配
  8. bean 的初始化(省略)

spring-bean 是如何設計的

sping-bean 的“設計圖”

和往常一樣,為了讓思路更連貫一些,我們還是按套路來:從宏觀到微觀。這里我畫了一張 spring-bean 的“設計圖”。

BeanFactoryUML_01

是不是看起來很復雜呢?單看這么多的類和線條確實挺復雜的,但我們知道,代碼實現都是對上層設計的具體化,我們經常會強調說,寫代碼前要先設計一下,也是這個道理,所以只要我們適當地分層、抽象,就可以從宏觀到微觀逐步地了解一個表面看起來非常復雜的體系。

如果你看過 spring 源碼就會發現,sping 的代碼不能算是優秀,但人家的抽象設計還是比較厲害的。

從使用者的角度看

首先,我們從使用者的角度來看這個設計圖:

  1. 如果我們把 sping-bean 當成全局上下文時,會使用SingletonBeanRegistry來存 bean,再用BeanFactory取 bean;
spring-bean-abstract01
  1. 如果我們把 spring-bean 當成對象工廠時,會使用BeanDefinitionRegistry注冊 beanDefinition,再用BeanFactory獲取 bean。
spring-bean-abstract02

也就是說,作為 spring-bean 的使用者,我們一般只會使用到三個接口:SingletonBeanRegistryBeanDefinitionRegistryBeanFactory

如果需要注冊類型轉換器、注冊處理器等等,頂多還會用到一個ConfigurableBeanFactory

這就是使用者眼里的 spring-bean。

從設計者的角度來看

接着,我們從設計者的角度來看。除了要提供上面的幾個接口,還需要提供更多的東西。

  1. 如果 spring-bean 被當成全局上下文,需要有一個地方來存放這些全局對象。
class DefaultSingletonBeanRegistry {
    // beanName=singletonObject鍵值對
    // 除了registerSingleton的會放在這里,registerBeanDefinition生成的單例bean實例也會放在這里
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
}
  1. 如果 spring-bean 被當成對象工廠,也需要有一個地方來存放 beanDefinition。
class DefaultListableBeanFactory {
    // beanName=beanDefination鍵值對
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
}
  1. 如果 spring-bean 被當成對象工廠,還需要根據 beanDefinition 的信息來創建 bean,這部分功能獨立成一個接口--AutowireCapableBeanFactory,用來進行實例化、裝配屬性、初始化等。

這就是設計者眼里的 spring-bean。

補充

現在回頭看看上面的“設計圖”,還會覺得很復雜嗎?

所以,研究源碼不要直接看代碼細節,那樣做會讓我們無從下手,我們應該從更上一層的設計入手,后面看源碼的時候將勢如破竹。另外,我們會發現,相比代碼實現,抽象的設計會更吸引人,更容易讓人記住。

接下來就是代碼實現了。這部分可能會枯燥一些,如果興趣不大,可以點到為止,理解抽象設計就足夠了。

開始看源碼--從哪里開始

spring-bean 的代碼非常多,把所有代碼都分析一遍非常難。這里我選擇分析獲取 bean 的過程,並且只關注單例 bean 的情況。起點從DefaultListableBeanFactory.getBean(Class)方法開始。

前面都是很多參數適配的方法,我們直接進入到DefaultListableBeanFactory.resolveBean(ResolvableType, Object[], boolean)。可以看到,beanFactory 是支持繼承關系的,如果兒子沒有,會嘗試從父親那里獲取。

    private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
        // 根據beanType從當前beanFactory里獲取bean
        NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
        if (namedBean != null) {
            return namedBean.getBeanInstance();
        }
        // 如果當前beanFactory里沒有,會嘗試從parent beanFactory中獲取
        // 這部分代碼省略······
        return null;
    }

bean沖突的處理

進入到DefaultListableBeanFactory.resolveNamedBean(ResolvableType, Object[], boolean)方法。這個方法主要做了這么一件事:處理 bean 沖突

前面的使用例子我們說過,當同一類型存在多個匹配的 bean 時,將出現NoUniqueBeanDefinitionException的異常,這時,我們可以有三種方法解決。在下面的代碼中就能夠體現出來。

    private <T> NamedBeanHolder<T> resolveNamedBean(
            ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {

        // 獲取指定類型的所有beanName,可能匹配到多個
        String[] candidateNames = getBeanNamesForType(requiredType);
        
        // 匹配到多個beanName的第一道處理。只保留兩種beanName:
        // 1. 通過registerSingleton注冊的
        // 2. 通過registerBeanDefinition注冊且autowireCandidate = true的
        if (candidateNames.length > 1) {
            List<String> autowireCandidates = new ArrayList<>(candidateNames.length);
            for (String beanName : candidateNames) {
                if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
                    autowireCandidates.add(beanName);
                }
            }
            if (!autowireCandidates.isEmpty()) {
                candidateNames = StringUtils.toStringArray(autowireCandidates);
            }
        }
        
        // 只有唯一匹配的beanName,那就根據beanName和beanType獲取bean
        if (candidateNames.length == 1) {
            String beanName = candidateNames[0];
            return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
        } else if (candidateNames.length > 1) {
            Map<String, Object> candidates = new LinkedHashMap<>(candidateNames.length);
            // 預處理,list轉map,其中value有兩種類型
            // 1. 通過registerSingleton注冊的且無參構造,則value為bean實例本身
            // 2. 其他情況,value為bean類型
            for (String beanName : candidateNames) {
                if (containsSingleton(beanName) && args == null) {
                    Object beanInstance = getBean(beanName);
                    candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));
                }
                else {
                    candidates.put(beanName, getType(beanName));
                }
            }
            // 匹配到多個beanName的第二道處理。找到唯一一個primary=true的beanName
            String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass());
            // 匹配到多個beanName的第三道處理。通過我們注冊的OrderComparator來比較獲得唯一beanName
            if (candidateName == null) {
                candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
            }
            // 只有唯一匹配的beanName,根據beanName和beanType獲取bean
            if (candidateName != null) {
                Object beanInstance = candidates.get(candidateName);
                if (beanInstance == null || beanInstance instanceof Class) {
                    beanInstance = getBean(candidateName, requiredType.toClass(), args);
                }
                return new NamedBeanHolder<>(candidateName, (T) beanInstance);
            }
            // 如果還是確定不了唯一beanName,根據nonUniqueAsNull選擇拋錯或返回null
            if (!nonUniqueAsNull) {
                throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
            }
        }
        
        return null;
    }

先看看是否需要創建

進入AbstractBeanFactory.doGetBean(String, Class<T>, Object[], boolean),這個方法代碼比較多,我只保留了單例部分的代碼。這個方法主要做了這么一件事:針對單例 bean,看看 bean 是否已經創建好了,創建好了就直接返回,沒有就走創建。

    protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
        // 轉義我們傳入的name,這里包括兩個內容:
        // 1. 如果是別名,需要轉換為別名對應的beanName;
        // 2. 如果是“&”+factoryBeanName,則需要去掉前面的“&”
        final String beanName = transformedBeanName(name);
        Object bean;

        // 獲取已經創建好的單例,這里包含兩種bean
        // 1. 完整的bean--從singletonObjects獲取
        // 2. 可能不完整的bean--從earlySingletonObjects或singletonFactories中獲取到的bean,只是實例化好,可能還沒初始化,設置還沒屬性裝配(我覺得這種機制意義不大)
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            // 返回bean實例
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        } else {
            // 這里標記對應的RootBeanDefinition需要重新merge(把RootBeanDefinition當成一個適配對象即可)
            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }

            // 獲取指定beanName對應的RootBeanDefinition對象
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

            // 如果當前bean需要依賴其他bean,則會先獲取依賴的bean
            // 這部分省略······

            // 根據scope創建bean,這里我們只看單例的情況
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, () -> {
                    // 進入創建bean
                    return createBean(beanName, mbd, args);
                });
                // 獲取bean實例
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            } else if (mbd.isPrototype()) {
                // ·······
            } else {
                // ·······
            }

        }

        // 如果獲取到的bean實例不是我們指定的類型
        if (requiredType != null && !requiredType.isInstance(bean)) {
            // 使用我們注冊的類型轉換器進行轉換
            T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            // 如果轉換不了,則會拋錯
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            return convertedBean;
        }
        return (T) bean;
    }

開始創建 bean

進入AbstractAutowireCapableBeanFactory.doCreateBean(String, RootBeanDefinition, Object[])。這個方法主要做了這么一件事:開始創建 bean,也可以說是定義了創建 bean 的一個主流程。

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
        // 實例化
        BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
        
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }

        // ······

        // 單例的可以將還沒裝配和初始化的bean先暴露出去,即放在singletonFactories中
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }
        
        Object exposedObject = bean;
        // 屬性裝配
        populateBean(beanName, mbd, instanceWrapper);
        // 初始化
        exposedObject = initializeBean(beanName, exposedObject, mbd);

        // ······
        return exposedObject;
    }

接下來是 bean 實例化、屬性裝配和初始化的內容,這部分代碼量相當龐大,隨便一項都可以分析個幾天幾夜,所以,這里不會講太細,而且只是簡單分析實例化和屬性裝配,不分析初始化。遺漏的地方感興趣可以自己研究下。

實例化

判斷走有參構造還是無參構造

進入AbstractAutowireCapableBeanFactory.createBeanInstance(String, RootBeanDefinition, Object[])。這個方法主要做了這么一件事:判斷走有參構造還是無參構造

    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        // 解析bean類型
        Class<?> beanClass = resolveBeanClass(mbd, beanName);
        
        // 通過beanDefinition中定義的Supplier來獲取實例化bean
        Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
        if (instanceSupplier != null) {
            return obtainFromSupplier(instanceSupplier, beanName);
        }
        // 通過beanDefinition中定義的FactoryMethod來實例化bean
        if (mbd.getFactoryMethodName() != null) {
            return instantiateUsingFactoryMethod(beanName, mbd, args);
        }
        
        // ······

        // 如果beanDefination的自動裝配模式為構造注入,或者beanDefination中指定了構造參數,或者我們傳入的構造參數非空,則進入實例化bean
        if (mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
            return autowireConstructor(beanName, mbd, null, args);
        }
        
        // ······

        // 使用無參構造實例化bean或factoryBean
        return instantiateBean(beanName, mbd);
    }

接下來,本文只討論有參構造實例化的情況

確定構造方法和參數列表

進入到ConstructorResolver.autowireConstructor(String, RootBeanDefinition, Constructor<?>[], Object[])。這個方法主要做了這么一件事:確定構造方法和參數列表。代碼太多了,相對地,我也刪減了很多,整體上會更好理解一些。這段代碼可以分成三種場景來看:

  1. 入參里顯式指定構造參數;
  2. beanDefinition 中指定了構造參數;
  3. beanDefinition 的自動裝配模式為構造注入。
    public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
            @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {

        BeanWrapperImpl bw = new BeanWrapperImpl();
        this.beanFactory.initBeanWrapper(bw);
        
        // 這兩個局部變量很重要,這整段代碼本質上就是為了確定這兩個變量的值,即使用哪個構造對象以及哪些參數列表來進行實例化
        Constructor<?> constructorToUse = null;
        Object[] argsToUse = null;

        
        // 場景1的情況,開局就知道了參數列表
        if (explicitArgs != null) {
            argsToUse = explicitArgs;
        }
        
        // 需要繼續確定constructorToUse和argsToUse的值
        if (constructorToUse == null || argsToUse == null) {
            Constructor<?>[] candidates = chosenCtors;
            // 一般chosenCtors都是null,這里我們利用反射初始化
            if (candidates == null) {
                Class<?> beanClass = mbd.getBeanClass();
                candidates = (mbd.isNonPublicAccessAllowed() ?
                            beanClass.getDeclaredConstructors() : beanClass.getConstructors());
            }
            
            // bean對象只有一個無參構造且未指定參數列表時,直接走無參實例化
            if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
                // ······
            }

            // 場景2和3的參數列表
            ConstructorArgumentValues resolvedValues = null;
            
            // 獲取參數列表中參數的個數
            int minNrOfArgs;
            // 場景1:入參指定的構造參數個數
            if (explicitArgs != null) {
                minNrOfArgs = explicitArgs.length;
            } else {
                // 場景2:beanDefinition中指定的構造參數個數
                // 場景3:0
                ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
                resolvedValues = new ConstructorArgumentValues();
                minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
            }
            // 根據參數列表的參數個數從大到小排列
            AutowireUtils.sortConstructors(candidates);
            
            // 判斷是否走場景3的邏輯
            boolean autowiring = (chosenCtors != null ||
                    mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
            // 遍歷候選的構造方法
            for (Constructor<?> candidate : candidates) {
                int parameterCount = candidate.getParameterCount();
                //  如果上一個循環已經確定了constructorToUse和argsToUse的值,或者參數個數已經無法匹配到對應的構造方法,則不再循環
                if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
                    break;
                }
                
                // 如果當前構造方法的參數個數小於minNrOfArgs,則遍歷下一個
                if (parameterCount < minNrOfArgs) {
                    continue;
                }

                ArgumentsHolder argsHolder;
                Class<?>[] paramTypes = candidate.getParameterTypes();
                // 針對場景2、3的情況
                if (resolvedValues != 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);
                } else {
                    // 場景1的情況
                    if (parameterCount != explicitArgs.length) {
                        continue;
                    }
                    argsHolder = new ArgumentsHolder(explicitArgs);
                }
                // 得到匹配的構造對象和構造參數
                constructorToUse = candidate;
                argsToUse = argsHolder.arguments;
            }
            // 如果找不到合適的構造對象,則會拋錯
            if (constructorToUse == null) {
                // 省略代碼······
            }
        }

        // 接下來就是使用構造對象和參數來實例化對象
        bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
        return bw;
    }

實例化部分的整個邏輯基本是講完了,很多的細節我都沒有展開講,但有了整體邏輯的引導,相信分析起來也不會很難。

屬性裝配

獲取待裝配屬性

進入AbstractAutowireCapableBeanFactory.populateBean(String, RootBeanDefinition, BeanWrapper)。這個方法主要做了這么一件事:獲取待裝配屬性。除了 beanDefinition 中定義的 name = value 待裝配屬性列表,還需要根據我們設置的自動裝配類型來額外添加 name = value 待裝配屬性列表。例如,選擇按名字裝配時,發現 bean 有 setFoo 方法,而且 beanFactory 里剛好也有叫 foo 的 bean,這時就會加入這樣一個待裝配屬性--name 為 foo,value為 foo 對應的 bean。

    protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {

        // 實例化后執行注冊的處理器中的方法(如果有的話),如果返回了false,則不進行屬性裝配,直接返回
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                        return;
                    }
                }
            }
        }
        
        // 獲取beanDefinition中定義的name=value待裝配屬性列表
        PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
        
        // 根據beanDefinition的自動裝配模式,在待裝配屬性列表里添加name = value
        int resolvedAutowireMode = mbd.getResolvedAutowireMode();
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
            if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }
            if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }
            pvs = newPvs;
        }
        
        // ······

        if (pvs != null) {
            // 執行屬性裝配
            applyPropertyValues(beanName, mbd, bw, pvs);
        }
    }

這個方法中主要涉及autowireByNameautowireByTypeapplyPropertyValues三個方法,前兩個暫時不展開,只講最后一個方法。

處理待裝配屬性的值

進入AbstractAutowireCapableBeanFactory.applyPropertyValues(String, BeanDefinition, BeanWrapper, PropertyValues)方法。這個方法主要做了這么一件事:處理待裝配屬性的值,具體需要經過“兩次轉換”,分別為:

  1. 如果 value 是BeanDefinitionBeanDefinitionHolder等的對象,需要轉換;
  2. 如果 value 的類型與 bean 中成員屬性的類型不一致,需要轉換。
    protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
        // ······

        MutablePropertyValues mpvs = null;
        
        // 獲取屬性列表
        List<PropertyValue> original;
        if (pvs instanceof MutablePropertyValues) {
            mpvs = (MutablePropertyValues) pvs;
            // 如果所有屬性對象已經完成“兩次轉換”,則直接裝配屬性
            if (mpvs.isConverted()) {
                bw.setPropertyValues(mpvs);
                return;
            }
            original = mpvs.getPropertyValueList();
        } else {
            original = Arrays.asList(pvs.getPropertyValues());
        }
        
        BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, bw);
        // 注意,這里並沒有進行所謂的深度復制,不要被命名迷惑了
        List<PropertyValue> deepCopy = new ArrayList<>(original.size());
        boolean resolveNecessary = false;
        // 遍歷屬性列表
        for (PropertyValue pv : original) {
            // 當前屬性對象已經完成“兩次轉換”,直接添加到deepCopy
            if (pv.isConverted()) {
                deepCopy.add(pv);
            } else {
                String propertyName = pv.getName();
                Object originalValue = pv.getValue();
                // 第一次轉換:例如value為BeanDefinition類型,需要轉換
                Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
                Object convertedValue = resolvedValue;
                // 如果當前屬性為可寫屬性,且屬性名不是類似於foo.bar或addresses[0]的形式,則需要進行第二次轉換
                boolean convertible = bw.isWritableProperty(propertyName) &&
                        !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
                if (convertible) {
                    // 第二次轉換:例如setter方法類型為Integer,此時value為string,需要轉換一下
                    convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
                }
                pv.setConvertedValue(convertedValue);
                // 當前屬性對象已經完成“兩次轉換”,添加到deepCopy
                deepCopy.add(pv);
            }
        }
        // 標記該屬性列表已經轉換過了,下次再用就不需要重復轉換
        if (mpvs != null && !resolveNecessary) {
            mpvs.setConverted();
        }

        // 屬性裝配
        bw.setPropertyValues(new MutablePropertyValues(deepCopy));

    }

propertyName的形式以及存取器propertyAccessor

在繼續分析源碼前,我們先來了解下屬性裝配的一些設計邏輯。在下面這個類中,存在三種成員屬性。

public class User {
    // 普通屬性
    private String name;
    private Integer age;
    
    // 對象屬性
    private Address address = new Address();
    
    // 集合屬性
    private List<String> hobbies = new ArrayList<>();
}

相應地,spring-bean 支持三種 propertyName 的格式:

// 普通形式的propertyName
rootBeanDefinition.getPropertyValues().add("name", "zzs001");
// 嵌套對象形式的propertyName,可以一直嵌套下去,例如:user.address.name
rootBeanDefinition.getPropertyValues().add("address.name", "波斯尼亞和黑塞哥維那");
// 索引形式的propertyName
rootBeanDefinition.getPropertyValues().add("hobbies[0]", "發呆");

那么,針對上面三種形式的 propertyName,spring-bean 如何存取對象的屬性呢?

在 spring-bean 中,使用org.springframework.beans.PropertyAccessor這個類來實現對成員屬性的存取

public interface PropertyAccessor {
    boolean isReadableProperty(String propertyName);
    boolean isWritableProperty(String propertyName);
    Object getPropertyValue(String propertyName) throws BeansException;
    void setPropertyValue(String propertyName, Object value) throws BeansException;
}

每個需要裝配的對象都有一個對應的 propertyAccessor。需要注意一點,propertyAccessor 只能存取當前綁定對象的“一級屬性”,例如,user 的 propertyAccessor 只能裝配address=new Address("波斯尼亞和黑塞哥維那"),而不能裝配address.name=波斯尼亞和黑塞哥維那。如果要存取 address.name,需要獲取到 address 的 propertyAccessor 來進行存取。這里還是用一段代碼來解釋吧,后面的源碼本質上也差不多。

// BeanWrapperImpl是`PropertyAccessor`,所以bw可以看成是user的存取器
BeanWrapperImpl bw = new BeanWrapperImpl();
bw.setBeanInstance(new User());

// 賦值name屬性需要用到的存取器,nestedPa01 == bw
AbstractNestablePropertyAccessor nestedPa01 = bw.getPropertyAccessorForPropertyPath("name");
nestedPa.setPropertyValue("name", "zzs001");
assertEquals(bw, nestedPa01);

// 賦值address.name屬性需要用到的存取器,nestedPa01 != bw
AbstractNestablePropertyAccessor nestedPa02 = bw.getPropertyAccessorForPropertyPath("address.name");
nestedPa.setPropertyValue("name", "波斯尼亞和黑塞哥維那");
assertEquals(bw, nestedPa02);

至於索引形式的處理,其實和普通屬性差不多。我就不展開了。

那么,我們繼續看源碼吧。

給屬性裝配值

進入AbstractNestablePropertyAccessor.setPropertyValue(String,Object)。這個方法做了這么一件事:給屬性裝配值。現在看看這段代碼是不是和我們的例子差不多呢?

public void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException {
    // 獲取propertyName對應的存取器
    // 如果緩存里有的話,將復用
    AbstractNestablePropertyAccessor nestedPa = getPropertyAccessorForPropertyPath(propertyName);
    // 創建PropertyTokenHolder對象,這里將解析“索引形式”的propertyName
    PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
    // 使用存取器進行賦值操作
    nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value));
}

那么,屬性裝配部分的分析就點到為止吧。

最后補充

以上簡單地分析了 spring-bean 的源碼。針對 getBean 的過程,本文未展開的內容包括:

  1. 創建多例 bean;
  2. 無參構造實例化;
  3. 屬性裝配中,根據自動裝配類型添加待裝配屬性;
  4. bean 的初始化。

后面有空我再做補充吧,感興趣的讀者也可以自行分析。另外,以上內容如有錯誤,歡迎指正。

最后,感謝閱讀。

2021-09-27更改

相關源碼請移步: spring-beans

本文為原創文章,轉載請附上原文出處鏈接:https://www.cnblogs.com/ZhangZiSheng001/p/13196228.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM