簡介
spring-bean 元件是 Spring IoC 的核心,我們可以使用它的 beanFactory 來獲取所需的物件,物件的例項化、屬性裝配和初始化等都可以交給 spring 來管理。 本文將從DefaultListableBeanFactory.getBean(Class)
方法開始分析獲取 bean 的過程,主要內容如下,由於篇幅較長,可以根據需要選擇閱讀:
- beanFactory 的設計
- 多個 beanName 的處理
- 獲取單例 bean
- 建立單例 bean
- bean 的例項化
- bean 的屬性裝配
- bean 的初始化(省略)
spring-bean 的原始碼比較多,有些不影響整體分析思路的程式碼會被省略掉(程式碼中會註明),另外,想要分析所有的程式碼可能不大現實,所以,針對部分內容,我會點到為止,例如,本文只分析單例 bean 而不分析多例 bean。
前篇回顧
上篇部落格Spring原始碼系列(一)--詳細介紹bean元件介紹了 bean 元件的一些重要理論概念,並通過例子演示如何使用 bean 元件。這裡回顧下,這幾個概念非常重要,是 bean 元件的理論基礎:
- 例項化、屬性裝配和初始化的概念。 例項化指建立出一個新的物件;屬性裝配指給物件的成員屬性賦值; 初始化指呼叫物件的初始化方法。
- 什麼是 bean:某個類的例項或描述物件,被註冊到了 Spring IoC 容器,這時通過 Spring IoC 容器獲取的這個類的物件就是 bean。
- 什麼是 beanFactory:一個工廠,用於註冊 bean 和獲取 bean。
- 什麼是 beanDefinition:一個描述物件,用來描述 bean 的例項化、屬性裝配、初始化等資訊。
beanFactory的設計
從客戶端來看,一個完整的 beanFactory 工廠包含以下基本功能:
- 註冊別名。對應下圖的
AliasRegistry
介面。 - 註冊單例物件。對應下圖的
SingletonBeanRegistry
介面。 - 註冊
BeanDefinition
物件。對應下圖的BeanDefinitionRegistry
介面。 - 獲取 bean。對應下圖的
BeanFactory
介面。
在 spring-bean 元件中,DefaultListableBeanFactory
就是一個完整的 beanFactory 工廠,也可以說是一個 IoC 容器。
BeanFactory
還有幾個擴充套件介面,用的比較多的可能是ConfigurableBeanFactory
和AutowireCapableBeanFactory
:
HierarchicalBeanFactory
用於提供父子工廠的支援。例如,當前 beanFactory 找不到 bean 時,會嘗試從 parent beanFactory 中獲取。ConfigurableBeanFactory
用於提供配置 beanFactory 的支援。例如,註冊BeanPostProcessor
、註冊TypeConverter
、註冊OrderComparator
等。ListableBeanFactory
用於提供批量獲取 bean 的支援(不包含父工廠的 bean)。例如,我們可以根據型別獲取 beanName-bean 的 map。AutowireCapableBeanFactory
用於提供例項化、屬性裝配、初始化等一系列管理 bean 生命週期的支援。 例如,該介面包含了 createBean、autowireBean、initializeBean、destroyBean 等方法。
當我們註冊 bean 時,根據註冊方式的不同,bean 的註冊資訊會被放入兩個不同的地方。
class DefaultSingletonBeanRegistry {
// beanName=singletonObject鍵值對
// 除了registerSingleton的會放在這裡,registerBeanDefinition生成的單例bean例項也會放在這裡
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
}
class DefaultListableBeanFactory {
// beanName=beanDefination鍵值對
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
}
接下來開始分析原始碼,註冊 bean 比較簡單,這裡就不看了,我們直接看 getBean(Class) 的程式碼。
從getBean(requiredType)方法開始
進入到 DefaultListableBeanFactory.getBean(Class)
方法,並逐漸展開。在DefaultListableBeanFactory.resolveBean(ResolvableType, Object[], boolean)
方法中,如果當前 beanFactory 中獲取不到這個 bean,將嘗試從 parent beanFactory 中獲取,這也說明了一點:父子 beanFactory 中允許存在相同 beanName 的 bean,只是獲取時當前 beanFactory 的優先順序更高一些。
public <T> T getBean(Class<T> requiredType) throws BeansException {
// 適配入參
// 可以看到,我們獲取bean時還可以指定構造引數
return getBean(requiredType, (Object[]) null);
}
public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
Assert.notNull(requiredType, "Required type must not be null");
// 繼續適配入參
// 這裡的第三個參數列示,如果指定型別對應的beanName不唯一時,true為返回null, false為丟擲異常
Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
// 如果獲取不到這個bean,丟擲異常
if (resolved == null) {
throw new NoSuchBeanDefinitionException(requiredType);
}
return (T) resolved;
}
private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
// 這裡的NamedBeanHolder就是簡單的對bean例項封裝了一層,不用太關注
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
// 如果獲取得到bean例項,則返回
if (namedBean != null) {
return namedBean.getBeanInstance();
}
// 如果沒有,嘗試從parent beanFactory中獲取
// 這部分程式碼省略······
return null;
}
存在多個beanName怎麼辦
通過 beanType 來獲取 bean,可能會存在一個型別關聯了多個 beanName 的情況,使用例子中我們說過,可以通過指定 beanDefination 的 isPrimary = true 或者註冊比較器的方式來解決。接下來我們看下具體的處理過程。
進入到DefaultListableBeanFactory.resolveNamedBean(ResolvableType, Object[], boolean)
方法。如果指定型別匹配到了多個 beanName,會進行以下處理:
- 如果存在通過
registerSingleton
註冊的 beanName,或者通過registerBeanDefinition
註冊且autowireCandidate = true
的 beanName,則僅保留它們,並剔除其他的 beanName; - 如果還是存在多個 beanName,檢查是否存在唯一一個通過
registerBeanDefinition
且isPrimary = true
的(存在多個會報錯),存在的話將它作為匹配到的唯一 beanName; - 如果還是存在多個 beanName,通過我們註冊的
OrderComparator
來確定優先值最小的作為唯一 beanName,注意,通過registerSingleton
註冊的和通過registerBeanDefinition
註冊的,比較的物件是不一樣的; - 如果還是存在多個 beanName,根據 nonUniqueAsNull,為 true 是返回 null,為 false 丟擲
NoUniqueBeanDefinitionException
異常。
private <T> NamedBeanHolder<T> resolveNamedBean(
ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {
Assert.notNull(requiredType, "Required type must not be null");
// 獲取指定型別的所有beanName,可能匹配到多個
String[] candidateNames = getBeanNamesForType(requiredType);
// 如果指定型別匹配到了多個beanName,進行以下操作:
// 如果存在通過registerSingleton註冊的beanName,或者通過registerBeanDefinition註冊且 autowireCandidate = true的beanName,則僅保留它們,並剔除其他的beanName;
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);
// 遍歷候選的beanName
for (String beanName : candidateNames) {
// 如果該beanName是通過registerSingleton註冊的,且傳入構造引數為空
// 則獲取該bean例項,並放入candidates
if (containsSingleton(beanName) && args == null) {
Object beanInstance = getBean(beanName);
candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));
}
else {
// 其他情況下,則獲取該beanName對應的型別,並放入candidates
// 注意,這裡的型別不一定是我們入參指定的型別,例如,如果我指定的是UserServiceFactoryBean.class,這裡返回的卻是UserService.class
candidates.put(beanName, getType(beanName));
}
}
// 如果裡面存在唯一一個通過registerBeanDefinition註冊的且isPrimary=true(存在多個會報錯),則將它作為匹配到的唯一beanName
String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass());
// 如果還是確定不了,則通過我們註冊的OrderComparator來判斷candidates中value的優先數,挑選優先數最小的value對應的key作為唯一的beanName
if (candidateName == null) {
candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
}
if (candidateName != null) {
Object beanInstance = candidates.get(candidateName);
// 如果candidates中的value本身就是一個bean例項,那麼直接返回就好了
// 如果不是,則根據beanName和beanType獲取bean
if (beanInstance == null || beanInstance instanceof Class) {
beanInstance = getBean(candidateName, requiredType.toClass(), args);
}
return new NamedBeanHolder<>(candidateName, (T) beanInstance);
}
// 如果還是確定不了唯一beanName,且設定了nonUniqueAsNull=false(預設為false),則會拋錯
if (!nonUniqueAsNull) {
throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
}
}
return null;
}
根據beanName和beanType獲取bean
進入AbstractBeanFactory.getBean(String, Class<T>, Object...)
。這個方法裡包括四個步驟:
- 轉義name。主要指的是當 name 是別名或者是 “&” + factory beanName 形式時進行轉義;
- 如果是單例 bean 且構造引數為空,則會從 singletonObjects 中獲取已生成的 bean,或者從 earlySingletonObjects/singletonFactories 中獲取已經例項化但可能還沒裝配或初始化的 bean。如果獲取到的不是 null,直接返回對應的 bean 例項;
- 如果當前 beanFactory 沒有指定的 beanName,則會去 parent beanFactory 中獲取;
- 如果當前 bean 需要依賴其他 bean,則會先獲取依賴的 bean;
- 根據 scope 選擇生成單例 bean 還是多例 bean;
- 進行型別檢查,如果獲取的 bean 不匹配,會先用我們註冊的型別轉換器轉換,如果還是不匹配就丟擲
BeanNotOfRequiredTypeException
。
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
throws BeansException {
// 適配入參
// 這裡最後一個引數指獲取的bean是否純粹用於型別檢查,如果是的話,beanFactory不會標記這個bean正在生成中,僅對單例bean有用
return doGetBean(name, requiredType, args, false);
}
@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 轉義我們傳入的name,這裡包括兩個內容:
// 1. 如果是別名,需要轉換為別名物件的beanName;
// 2. 如果是“&”+factoryBeanName,則需要去掉前面的“&”
final String beanName = transformedBeanName(name);
Object bean;
// 獲取單例
// 注意,這裡獲取到的有可能是已經初始化,也有可能是還沒初始化,甚至還沒裝配的bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 省略日誌部分······
// 獲取bean,因為sharedInstance有可能是factoryBean,如果我們要的是factoryBean對應的bean,則還要getObject
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 如果當前執行緒已經在生成beanName對應的bean,就會拋錯
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 如果當前beanFactory沒有指定的beanName,則會去parent beanFactory中獲取
// 這部分省略······
// 這裡標記指定bean正在建立中,一般對單例bean才有意義
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// 獲取指定beanName對應的RootBeanDefinition物件
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 檢查RootBeanDefinition,目前就是檢查是否對應的型別為抽象類,是的話拋錯
checkMergedBeanDefinition(mbd, beanName, args);
// 如果當前bean需要依賴其他bean,則會先獲取依賴的bean
// 這部分省略······
// 建立單例bean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
// 進入建立bean或factoryBean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
// 獲取bean例項
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 建立多例bean
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
// 標記當前執行緒正在建立這個bean
beforePrototypeCreation(beanName);
// 進入建立bean或factoryBean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// 去掉當前執行緒中這個bean正在建立的標記
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// 接下來這種一般是自定義Scope的情況,這裡省略不討論
else {
// ·······
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// 如果獲取到的bean例項不是我們指定的型別
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
// 使用我們註冊的型別轉換器進行轉換
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
// 如果轉換不了,則會拋錯
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
由於單例 bean 和多例 bean 的建立差不多,本文只選擇單例的來分析。
獲取單例bean
進入DefaultSingletonBeanRegistry.getSingleton(String, ObjectFactory)
。這個方法包括幾個過程,主要就是處理一些多執行緒問題:
- 獲取指定 beanName 的 bean,如果已經存在,就不去建立,這時為了處理多執行緒同時建立 bean 的問題;
- 如果當前 bean 已經在建立中,會丟擲 BeanCurrentlyInCreationException,建立單例 bean 之前是有加鎖的,按理不會出現這種情況;
- 建立單例 bean;
- 如果建立成功,將 bean 例項加入 singletonObjects,並且刪除掉 singletonFactories 和 earlySingletonObjects 中對應的鍵值對。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 這裡我不是很理解,為什麼使用singletonObjects作為鎖
// 因為從earlySingletonObjects/singletonFactories中獲取已經例項化但可能還沒裝配或初始化的 bean時,用的鎖也是singletonObjects,這樣的話,提前暴露的機制好像就廢掉了啊???TODO
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 如果當前beanFactory的單例正在銷燬,則不允許建立單例
if (this.singletonsCurrentlyInDestruction) {
// 省略拋錯······
}
// 判斷當前bean是不是已經在建立中,是的話丟擲BeanCurrentlyInCreationException
// 由於加了鎖,這種情況應該是不會發生的
beforeSingletonCreation(beanName);
boolean newSingleton = false;
// 省略部分程式碼······
try {
// 這裡的執行的是createBean方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
// 這種情況我不是很理解,singletonObjects的操作不應該被鎖住了嗎?TODO
catch (IllegalStateException ex) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
// 如果丟擲的是BeanCreationException,
catch (BeanCreationException ex) {
// 省略部分程式碼······
throw ex;
}
finally {
// 省略部分程式碼······
// 如果當前bean不處於建立狀態中,會丟擲IllegalStateException
afterSingletonCreation(beanName);
}
// 如果建立成功,將bean例項加入singletonObjects,並且刪除掉singletonFactories和earlySingletonObjects中對應的鍵值對
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
以上方法中,如果獲取不到已生成的單例 bean,則會開始建立 bean。
建立單例bean
進入AbstractAutowireCapableBeanFactory.createBean(String, RootBeanDefinition, Object[])
。這個方法包括以下過程:
- 解析 beanType,並且再次包裝
RootBeanDefinition
; - 執行我們註冊的
InstantiationAwareBeanPostProcessor
的postProcessBeforeInstantiation
方法,如果返回了非空物件,則將其返回。也就是說我們可以在該方法中自定義完成 bean 的例項化、裝配和初始化。 - 建立 bean。
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
RootBeanDefinition mbdToUse = mbd;
// 解析當前RootBeanDefinition對應生成的bean型別,並進行再次包裝
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// 省略部分程式碼······
try {
// 執行我們註冊的InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法。也就是說我們可以在該方法中自定義完成 bean 的例項化、裝配和初始化。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
// 如果該方法返回bean,那就直接返回
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);
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
進入AbstractAutowireCapableBeanFactory.doCreateBean(String, RootBeanDefinition, Object[])
。這個方法主要包含以下過程:
- 例項化 bean;
- 執行我們註冊的
MergedBeanDefinitionPostProcessor
的postProcessMergedBeanDefinition
方法; - 如果是單例,將還沒裝配和初始化的 bean 先暴露出去,即放在singletonFactories中,如果其他執行緒進來獲取,可以將這個 bean 或 factoryBean 返回,而不需要等待;
- 屬性裝配;
- 初始化;
- 將生成的 bean 放入 disposableBeans 中。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
// 例項化
// 如果是單例,嘗試從factoryBeanInstanceCache中獲取
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// 例項化bean
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 執行我們註冊的MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// 單例的可以將還沒裝配和初始化的bean先暴露出去,即放在singletonFactories中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
// 屬性裝配
populateBean(beanName, mbd, instanceWrapper);
// 初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
// 省略部分程式碼······
// 將生成的bean或factoryBean放入disposableBeans中
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
接下來將展開 bean 的例項化、屬性裝配和初始化。其中,例項化和屬性裝配的程式碼比較複雜,我們重點分析,至於初始化部分,則留給讀者自行閱讀。
例項化
進入AbstractAutowireCapableBeanFactory.createBeanInstance(String, RootBeanDefinition, Object[])
。這個方法主要過程如下:
- 解析 beanType,並對 beanType 進行一些必要的檢查;
- 通過我們設定的 InstanceSupplier 或 FactoryMethod 來直接獲取 bean,如果有的話,直接返回該物件;
- 如果構造引數為空,則可以複用已經解析好的構造物件(如果有的話);
- 執行我們註冊的
SmartInstantiationAwareBeanPostProcessor
的determineCandidateConstructors
獲取構造物件陣列; - 如果得到的陣列不是空,或者 beanDefination 的裝配模式為構造注入,或者 beanDefination 包含構造引數,或者我們傳入的構造引數非空,則進入例項化 bean
- 其他情況,使用無參構造來例項化。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 解析bean型別
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// 如果bean型別不是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());
}
// 通過RootBeanDefinition中定義的Supplier來獲取例項化bean
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// 通過RootBeanDefinition中定義FactoryMethod來例項化bean
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// 如果構造引數為空,則可以複用已經解析好的構造物件(如果有的話)
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
// 執行我們註冊的SmartInstantiationAwareBeanPostProcessor的determineCandidateConstructors獲取Constructor物件陣列(如果有的話)
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 如果得到的陣列不是空,或者beanDefination的裝配模式為構造注入,或者beanDefination包含構造引數,或者我們傳入的構造引數非空,則進入例項化bean或factoryBean
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// 省略部分程式碼······
// 使用無參構造例項化bean或factoryBean
return instantiateBean(beanName, mbd);
}
例項化的方法包括有參構造例項化和無參構造例項化兩種,本文只討論有參構造例項化的情況。
ConstructorArgumentValues和ArgumentsHolder
在繼續分析之前,有必要了解下ConstructorArgumentValues
和ArgumentsHolder
這兩個類。
首先,ConstructorArgumentValues
用於定義構造方法的引數列表的值。spring 中,ConstructorArgumentValues
一般被定義在 BeanDefinition
物件中,它影響著 bean 的例項化,是 bean 例項化時選擇構造物件的依據。
public class ConstructorArgumentValues {
// 索引+引數值
// 例如,對應new User(int age, String name, String address)的構造方法,可以包含元素:0=new ValueHolder(18),2=new ValueHolder("北京")
private final Map<Integer, ValueHolder> indexedArgumentValues = new LinkedHashMap<>();
// 通用引數值
// 例如,對應new User(int age, String name, String address)的構造方法,如果indexedArgumentValues中不包含name的值,則可以在genericArgumentValues中查詢,我們只需要新增元素:new ValueHolder("zzs001", String.class)
private final List<ValueHolder> genericArgumentValues = new ArrayList<>();
// 內部類,代表一個引數的值
public static class ValueHolder implements BeanMetadataElement {
@Nullable
private Object value;
@Nullable
private String type;
@Nullable
private String name;
@Nullable
private Object source;
private boolean converted = false;
}
ArgumentsHolder
是ConstructorResolver
的內部類,和ConstructorArgumentValues
一樣,它也是用來定義構造方法的引數列表的值,區別在於,ConstructorArgumentValues
的值是“未解析的”,而ArgumentsHolder
包含了“未解析”(preparedArguments)、“解析未完成”(rawArguments)和"解析完成"(arguments)三種值。
為什麼會這樣呢?因為ConstructorArgumentValues
中的引數值的型別不一定和構造方法中的匹配,包括兩種情況:
- 型別不同,但可以通過
TypeConverter
轉換的型別。例如,在new User(int age, String name, Address address)
的構造方法中,我可以在ConstructorArgumentValues
新增2=new AddressVO()
,這個時候只要 spring 能找到合適的轉換器就能轉換,這個轉換過程為:“解析未完成”(rawArguments) --》 "解析完成"(arguments)。 - 型別不同,引數的值指向其他 bean ,當然也可以是其他 spring 可識別的引用。例如,
new User(int age, String name, Address address)
的構造方法中,我可以在ConstructorArgumentValues
新增2=new RootBeanDefinition(Address.class)
,這個轉換過程為:“未解析”(preparedArguments) --》“解析未完成”(rawArguments)。
private static class ArgumentsHolder {
public final Object[] rawArguments;
public final Object[] arguments;
public final Object[] preparedArguments;
public boolean resolveNecessary = false;
}
理解完這兩個類之後,我們繼續分析例項化的原始碼。
有參構造例項化
進入到AbstractAutowireCapableBeanFactory.autowireConstructor(String, RootBeanDefinition, Constructor<?>[], Object[])
方法。這裡建立了一個ConstructorResolver
物件並直接呼叫它的 autowireConstructor 方法。
protected BeanWrapper autowireConstructor(
String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
進入ConstructorResolver.autowireConstructor(String, RootBeanDefinition, Constructor<?>[], Object[])
。這個方法程式碼比較多,為了更好地理解,可以分成兩種場景來看:
- 入參裡顯式指定構造引數。這種場景的引數值預設都是解析過的,所以不需要解析,該場景要求對應的構造物件的引數數量必須和指定的一樣。
BeanDefinition
物件中指定ConstructorArgumentValues
。這種場景的引數值需要經過兩步轉換,該場景要求對應的構造物件的引數數量不小於指定的數量。
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);
// 定義最終用於例項化物件的構造器
Constructor<?> constructorToUse = null;
// 定義存放(“未解析”、“解析未完成”、“解析完成”)構造引數的物件
ArgumentsHolder argsHolderToUse = null;
// 定義最終用於例項化物件的構造引數
Object[] argsToUse = null;
// 入參顯式宣告瞭構造引數(場景一),則不需要解析引數列表值,但需解析構造物件
if (explicitArgs != null) {
argsToUse = explicitArgs;
}
else {
Object[] argsToResolve = null;
// BeanDefinition物件中指定ConstructorArgumentValues(場景二),如果引數列表值或構造物件已經解析,則不需要再解析
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;
}
}
}
if (argsToResolve != null) {
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
}
}
// 進入解析引數列表值和構造物件
if (constructorToUse == null || argsToUse == null) {
// 如果入參裡沒有顯式指定構造物件的陣列,使用反射方式獲取
Constructor<?>[] candidates = chosenCtors;
if (candidates == null) {
Class<?> beanClass = mbd.getBeanClass();
try {
// BeanDefinition中可以定義是否包括非public的方法
candidates = (mbd.isNonPublicAccessAllowed() ?
beanClass.getDeclaredConstructors() : beanClass.getConstructors());
}
catch (Throwable ex) {
// 省略程式碼······
}
}
// 如果陣列中只有一個無參構造,且入參和BeanDefinition中都未指定引數列表值,則標記該BeanDefinition物件的構造引數已解析,並例項化bean
if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
// 省略程式碼······
}
// 判斷是否需要解析構造
boolean autowiring = (chosenCtors != null ||
mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
// 這裡存放“解析未完成”的引數列表值
ConstructorArgumentValues resolvedValues = null;
// 獲取要求構造引數的最小數量
int minNrOfArgs;
// 入參顯式宣告瞭構造引數(場景一),minNrOfArgs即為指定陣列的長度
if (explicitArgs != null) {
minNrOfArgs = explicitArgs.length;
}
else {
// BeanDefinition物件中指定ConstructorArgumentValues(場景二),則需要計算minNrOfArgs,並進行“未解析” --> “解析未完成”的轉換
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
// 根據引數數量從小到大排列
AutowireUtils.sortConstructors(candidates);
int minTypeDiffWeight = Integer.MAX_VALUE;
Set<Constructor<?>> ambiguousConstructors = null;
LinkedList<UnsatisfiedDependencyException> causes = null;
// 遍歷候選的構造物件
for (Constructor<?> candidate : candidates) {
// 獲取當前構造物件的引數數量
int parameterCount = candidate.getParameterCount();
// 如果上一個迴圈已經找到匹配的構造物件,則跳出迴圈1
if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
break;
}
// 如果當前構造物件的引數數量小於minNrOfArgs,則遍歷下一個
// 注意,入參裡顯式指定構造引數(場景一)要求對應的構造物件的引數數量必須和指定的一樣。BeanDefinition物件中指定ConstructorArgumentValues(場景二)要求對應的構造物件的引數數量不小於指定的數量
if (parameterCount < minNrOfArgs) {
continue;
}
ArgumentsHolder argsHolder;
// 獲取當前構造物件的引數型別陣列
Class<?>[] paramTypes = candidate.getParameterTypes();
// BeanDefinition物件中指定ConstructorArgumentValues(場景二)的情況
if (resolvedValues != null) {
// 進行“解析未完成”->“解析完成”的轉換
try {
// 這裡是為了處理JDK6的ConstructorProperties註解,其他情況都會返回null。
String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
if (paramNames == null) {
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
// 獲取當前構造物件的引數名陣列
paramNames = pnd.getParameterNames(candidate);
}
}
// 建立ArgumentsHolder物件
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
}
catch (UnsatisfiedDependencyException ex) {
// 省略程式碼······
continue;
}
}
// 入參裡顯式指定構造引數(場景一)的情況
else {
// 如果當前構造引數的數量小於指定引數的數量,則繼續迴圈
if (parameterCount != explicitArgs.length) {
continue;
}
// 建立ArgumentsHolder物件,因為不需要解析引數,所以,這種情況raw、prepared、resolved都是一樣的
argsHolder = new ArgumentsHolder(explicitArgs);
}
// 計算指定引數和當前構造的引數型別的差異值
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// 差異值小於閾值
if (typeDiffWeight < minTypeDiffWeight) {
// 得到匹配的構造物件和構造引數
constructorToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousConstructors = null;
}
// 差異值大於閾值,這種不考慮
else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
// 省略程式碼······
}
}
// 如果找不到合適的構造物件,則會拋錯
if (constructorToUse == null) {
// 省略程式碼······
}
else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
// 省略程式碼······
}
// BeanDefinition物件中指定ConstructorArgumentValues(場景二),為了複用解析好的構造和引數列表,需要標記當前BeanDefinition的構造引數已解析
if (explicitArgs == null && argsHolderToUse != null) {
argsHolderToUse.storeCache(mbd, constructorToUse);
}
}
Assert.state(argsToUse != null, "Unresolved constructor arguments");
// 接下來就是使用構造物件和引數來例項化物件,就不往下看了。
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
return bw;
}
例項化部分比較難,主要還得先理解一些抽象概念,例如:兩個場景、引數的轉換等。
屬性裝配
進入AbstractAutowireCapableBeanFactory.populateBean(String, RootBeanDefinition, BeanWrapper)
。這個方法包括以下過程:
- 執行我們註冊的
InstantiationAwareBeanPostProcessor
的postProcessAfterInstantiation
方法,如果返回了 false,則不進行屬性裝配,直接返回; - 獲取 beanDefinition 中的
PropertyValues
物件,根據 beanDefinition 設定的注入型別,填充PropertyValues
物件; - 執行我們註冊的
InstantiationAwareBeanPostProcessor
的postProcessProperties
方法,可以對PropertyValues
物件進行修改; - 依賴檢查(如果設定了);
- 進行屬性裝配。
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// 如果例項物件為空,則丟擲異常或直接返回
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
return;
}
}
// 執行我們註冊的InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法,如果返回了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物件中的PropertyValues,包含了name=value的PropertyValue物件的列表
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
// 根據我們設定的注入方式,填充
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;
}
// beanFactory中是否註冊了InstantiationAwareBeanPostProcessors
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
// BeanDefinition物件中是否設定了依賴檢查
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
// 如果為空,再次從BeanDefinition物件中獲取,TODO?
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 執行我們註冊的InstantiationAwareBeanPostProcessor的postProcessProperties方法,可以對PropertyValues物件進行修改
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
// 省略部分程式碼······
pvs = pvsToUse;
}
}
}
// 如果BeanDefinition物件中設定了依賴檢查,則需要檢查依賴設定
if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
if (pvs != null) {
// 執行屬性裝配
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
這個方法中主要涉及autowireByName
、autowireByType
和applyPropertyValues
三個方法,前兩個暫時不展開,只講最後一個方法。
幾個重要的知識點
在分析applyPropertyValues
方法之前,我們需要知道一下幾個知識點。這裡以User
這個類來展開例子。
public class User {
private String name;
private int age;
private Address address;
private List<String> hobbies;
}
class Address {
private String region;
private String desc;
}
propertyName的幾種形式
當我們給 beanDefinition設定屬性值時,一般都會這樣採用這樣的賦值,這裡成為“普通形式”。
rootBeanDefinition.getPropertyValues().add("name", "zzs001");
rootBeanDefinition.getPropertyValues().add("age", 18);
rootBeanDefinition.getPropertyValues().add("address", new Address("", ""));
rootBeanDefinition.getPropertyValues().add("hobbies", new ArrayList());
針對型別為 object、list、array、map 等成員屬性,spring 還支援其他的賦值方式,如下,分別成為“巢狀物件形式”和“索引形式”:
// 巢狀物件形式
rootBeanDefinition.getPropertyValues().add("address.region", "");
rootBeanDefinition.getPropertyValues().add("address.desc", "");
// 索引形式
rootBeanDefinition.getPropertyValues().add("hobbies[0]", "");
正是由於 propertyName 引入了多種的形式,所以,原本簡單的賦值行為被搞得非常複雜。例如,巢狀物件形式還可以是這樣:foo.user.address.region
,幾乎可以一直巢狀下去。
PropertyAccessor
propertyAccessor 物件一般繫結了一個例項物件,通過PropertyAccessor
介面的方法可以對物件的屬性進行存取操作。屬性裝配中最終對成員屬性賦值就是呼叫它的setPropertyValue
方法。AbstractNestablePropertyAccessor
中維護了一個 map,key 為當前繫結物件的屬性名(不包含巢狀和索引),value 就是對於的PropertyAccessor
物件。
public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyAccessor {
private Map<String, AbstractNestablePropertyAccessor> nestedPropertyAccessors;
}
在上面的例子中,
rootBeanDefinition.getPropertyValues().add("name", "zzs001");
rootBeanDefinition.getPropertyValues().add("age", 18);
這種形式共用一個繫結了User
型別例項的PropertyAccessor
物件。
// 巢狀物件形式
rootBeanDefinition.getPropertyValues().add("address.region", "");
rootBeanDefinition.getPropertyValues().add("address.desc", "");
這種形式共用一個繫結了Address
型別例項的PropertyAccessor
物件,該物件和"address"這個名字關聯起來維護在 nestedPropertyAccessors 中。
// 索引形式
rootBeanDefinition.getPropertyValues().add("hobbies[0]", "");
這種形式也是一個繫結了User
型別例項的PropertyAccessor
物件,該物件和"hobbies"這個名字關聯起來維護在 nestedPropertyAccessors 中。
PropertyTokenHolder
PropertyTokenHolder
是AbstractNestablePropertyAccessor
的內部類,它更多的是針對“索引形式”的 propertyName。例如,"hobbies[0]"對於的PropertyTokenHolder
中,actualName = hobbies,canonicalName = [0],keys = {0}。
protected static class PropertyTokenHolder {
public PropertyTokenHolder(String name) {
this.actualName = name;
this.canonicalName = name;
}
public String actualName;
public String canonicalName;
@Nullable
public String[] keys;
}
接下來繼續分析屬性裝配的程式碼。
applyPropertyValues
進入AbstractAutowireCapableBeanFactory.applyPropertyValues(String, BeanDefinition, BeanWrapper, PropertyValues)
方法。和構造引數一樣,設定成員屬性的引數也需要經過“兩次轉換”,這裡就不詳細講解。這個方法主要包括以下過程:
- 獲取屬性物件列表,如果這個列表的屬性物件都已經完成“兩次轉換”,則直接裝配屬性;
- 遍歷屬性物件列表,分別進行兩次轉換,如果列表中沒有類似
BeanDefinition
、BeanDefinitionHolder
等的物件,則設定PropertyValues
物件已經轉換完成,下次呼叫這個方法不用再進行轉換; - 屬性裝配。
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
// 如果沒有需要注入的屬性,直接返回
if (pvs.isEmpty()) {
return;
}
// 省略部分程式碼······
MutablePropertyValues mpvs = null;
// 獲取屬性物件列表
List<PropertyValue> original;
if (pvs instanceof MutablePropertyValues) {
mpvs = (MutablePropertyValues) pvs;
// 如果所有屬性物件已經完成“兩次轉換”,則直接裝配屬性
if (mpvs.isConverted()) {
try {
bw.setPropertyValues(mpvs);
return;
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
original = mpvs.getPropertyValueList();
}
else {
original = Arrays.asList(pvs.getPropertyValues());
}
// 獲取我們註冊的型別轉換器
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
// 建立第一次轉換所用的解析器
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// 定義一個列表,用於存放完成“兩次轉換”的屬性物件
// 這注意,這裡並沒有進行所謂的複製,不要被命名迷惑了
List<PropertyValue> deepCopy = new ArrayList<>(original.size());
boolean resolveNecessary = false;
// 遍歷屬性物件
for (PropertyValue pv : original) {
// 當前屬性物件已經完成“兩次轉換”,直接新增到列表
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {
String propertyName = pv.getName();
Object originalValue = pv.getValue();
// 省略部分程式碼······
// 第一次轉換
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
// 如果當前屬性為可寫屬性,且屬性名不是類似於foo.bar或addresses[0]的形式,則需要進行第二次轉換
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
if (convertible) {
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
// 如果轉換後的屬性物件和初始物件一樣,一般指的是普通物件,而不是BeanDefinition、BeanDefinitionHolder等
if (resolvedValue == originalValue) {
// 如果需要第二次轉換,則設定複用的目標物件
if (convertible) {
pv.setConvertedValue(convertedValue);
}
// 將原屬性物件新增到列表
deepCopy.add(pv);
}
// 這種情況不考慮
else if (convertible && originalValue instanceof TypedStringValue &&
!((TypedStringValue) originalValue).isDynamic() &&
!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
pv.setConvertedValue(convertedValue);
deepCopy.add(pv);
}
// 其他情況
else {
// 標記每次都需要解析
resolveNecessary = true;
// 將原屬性物件新增到列表
deepCopy.add(new PropertyValue(pv, convertedValue));
}
}
}
// 如果不包含BeanDefinition、BeanDefinitionHolder等物件,則設定PropertyValues為已轉換,這樣下次呼叫這個方法,就不需要進行任何的轉換了
if (mpvs != null && !resolveNecessary) {
mpvs.setConverted();
}
// 屬性裝配
try {
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
進入AbstractPropertyAccessor.setPropertyValues(PropertyValues)
方法。這裡遍歷屬性物件列表,逐個進賦值操作。
public void setPropertyValues(PropertyValues pvs) throws BeansException {
// 入參適配
// 後面兩個引數分別代表:是否忽略NotWritablePropertyException異常、是否忽略NullValueInNestedPathException異常
setPropertyValues(pvs, false, false);
}
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException {
// 獲取屬性物件列表
List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
for (PropertyValue pv : propertyValues) {
// 省略try-catch的程式碼和其他異常相關的程式碼······
setPropertyValue(pv);
}
}
setPropertyValue
進入AbstractNestablePropertyAccessor.setPropertyValue(PropertyValue)
。這個方法包括以下過程:
- 獲取 propertyName 對應的
PropertyAccessor
物件,這裡將解析“巢狀物件形式”的 propertyName; - 建立
PropertyTokenHolder
物件,這裡將解析“索引形式”的 propertyName; - 使用
PropertyAccessor
物件進行賦值操作。
public void setPropertyValue(PropertyValue pv) throws BeansException {
// 適配入參
setPropertyValue(pv.getName(), pv.getValue());
}
public void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException {
AbstractNestablePropertyAccessor nestedPa;
try {
// 獲取propertyName對應的PropertyAccessor物件,這裡將解析“巢狀物件形式”的propertyName
// 如果快取裡有的話,將複用
nestedPa = getPropertyAccessorForPropertyPath(propertyName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Nested property in path '" + propertyName + "' does not exist", ex);
}
// 建立PropertyTokenHolder物件,這裡將解析“索引形式”的propertyName
PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
// 使用PropertyAccessor物件進行賦值操作
nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value));
}
進入AbstractNestablePropertyAccessor.setPropertyValue(PropertyTokenHolder, PropertyValue)
方法。這裡根據 propertyName 是否為“索引形式”呼叫不同的方法。
protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
if (tokens.keys != null) {
processKeyedProperty(tokens, pv);
}
else {
processLocalProperty(tokens, pv);
}
}
這裡我們不看 propertyName 為“索引形式”的方法,只看processLocalProperty
。
private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
// 獲取actualName對應的PropertyHandler物件,如果有快取則複用
PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
if (ph == null || !ph.isWritable()) {
// 省略部分程式碼······
}
Object oldValue = null;
try {
Object originalValue = pv.getValue();
Object valueToApply = originalValue;
if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
// 因為我們的屬性引數都是轉換過的,所以這裡不再看轉換的程式碼
if (pv.isConverted()) {
valueToApply = pv.getConvertedValue();
}
else {
// 省略部分程式碼······
}
pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
}
// 接下來就是通過反射方式給屬性賦值,後續再展開
ph.setValue(valueToApply);
}
catch (Exception ex) {
// 省略部分程式碼······
}
}
屬性裝配的程式碼分析就點到為止吧。
最後補充
以上基本看完 spring-bean 的原始碼。針對 getBean 的過程,本文未展開的內容包括:
- 獲取和建立多例 bean;
- 無參構造例項化;
- 屬性裝配中,屬性值列表的填充(autowireByName和autowireByType)、屬性名為索引形式的屬性裝配
- bean 的初始化。
感興趣的讀者可以自行分析。另外,以上內容如有錯誤,歡迎指正。
最後,感謝閱讀。
相關原始碼請移步: spring-beans
本文為原創文章,轉載請附上原文出處連結:https://www.cnblogs.com/ZhangZiSheng001/p/13196228.html