簡介
在上一篇博客(Spring源碼系列(一)--詳細介紹bean組件)中,我們討論了 spring-bean 是什么?用來解決什么問題?如何使用 spring-bean?等等問題,算是從使用者的角度對 spring-bean 有了一定了解。這篇博客我們將開始分析 spring-bean 的源碼,大致的思路如下:
- spring-bean 是如何設計的
- 開始看源碼--從哪里開始
- bean 沖突的處理
- 先看看是否需要創建
- 開始創建 bean
- bean 的實例化
- bean 的屬性裝配
- bean 的初始化(省略)
spring-bean 是如何設計的
sping-bean 的“設計圖”
和往常一樣,為了讓思路更連貫一些,我們還是按套路來:從宏觀到微觀。這里我畫了一張 spring-bean 的“設計圖”。
是不是看起來很復雜呢?單看這么多的類和線條確實挺復雜的,但我們知道,代碼實現都是對上層設計的具體化,我們經常會強調說,寫代碼前要先設計一下,也是這個道理,所以只要我們適當地分層、抽象,就可以從宏觀到微觀逐步地了解一個表面看起來非常復雜的體系。
如果你看過 spring 源碼就會發現,sping 的代碼不能算是優秀,但人家的抽象設計還是比較厲害的。
從使用者的角度看
首先,我們從使用者的角度來看這個設計圖:
- 如果我們把 sping-bean 當成全局上下文時,會使用
SingletonBeanRegistry
來存 bean,再用BeanFactory
取 bean;

- 如果我們把 spring-bean 當成對象工廠時,會使用
BeanDefinitionRegistry
注冊 beanDefinition,再用BeanFactory
獲取 bean。

也就是說,作為 spring-bean 的使用者,我們一般只會使用到三個接口:SingletonBeanRegistry
、BeanDefinitionRegistry
和BeanFactory
。
如果需要注冊類型轉換器、注冊處理器等等,頂多還會用到一個ConfigurableBeanFactory
。
這就是使用者眼里的 spring-bean。
從設計者的角度來看
接着,我們從設計者的角度來看。除了要提供上面的幾個接口,還需要提供更多的東西。
- 如果 spring-bean 被當成全局上下文,需要有一個地方來存放這些全局對象。
class DefaultSingletonBeanRegistry {
// beanName=singletonObject鍵值對
// 除了registerSingleton的會放在這里,registerBeanDefinition生成的單例bean實例也會放在這里
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
}
- 如果 spring-bean 被當成對象工廠,也需要有一個地方來存放 beanDefinition。
class DefaultListableBeanFactory {
// beanName=beanDefination鍵值對
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
}
- 如果 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[])
。這個方法主要做了這么一件事:確定構造方法和參數列表。代碼太多了,相對地,我也刪減了很多,整體上會更好理解一些。這段代碼可以分成三種場景來看:
- 入參里顯式指定構造參數;
- beanDefinition 中指定了構造參數;
- 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);
}
}
這個方法中主要涉及autowireByName
、autowireByType
和applyPropertyValues
三個方法,前兩個暫時不展開,只講最后一個方法。
處理待裝配屬性的值
進入AbstractAutowireCapableBeanFactory.applyPropertyValues(String, BeanDefinition, BeanWrapper, PropertyValues)
方法。這個方法主要做了這么一件事:處理待裝配屬性的值,具體需要經過“兩次轉換”,分別為:
- 如果 value 是
BeanDefinition
、BeanDefinitionHolder
等的對象,需要轉換; - 如果 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 的過程,本文未展開的內容包括:
- 創建多例 bean;
- 無參構造實例化;
- 屬性裝配中,根據自動裝配類型添加待裝配屬性;
- bean 的初始化。
后面有空我再做補充吧,感興趣的讀者也可以自行分析。另外,以上內容如有錯誤,歡迎指正。
最后,感謝閱讀。
2021-09-27更改
相關源碼請移步: spring-beans
本文為原創文章,轉載請附上原文出處鏈接:https://www.cnblogs.com/ZhangZiSheng001/p/13196228.html