1. 程式人生 > >Spring原始碼系列(二)--bean元件的原始碼分析

Spring原始碼系列(二)--bean元件的原始碼分析

# 簡介 spring-bean 元件是 Spring IoC 的核心,我們可以使用它的 beanFactory 來獲取所需的物件,物件的例項化、屬性裝配和初始化等都可以交給 spring 來管理。 本文將從`DefaultListableBeanFactory.getBean(Class)`方法開始分析獲取 bean 的過程,主要內容如下,由於篇幅較長,可以根據需要選擇閱讀: 1. beanFactory 的設計 2. 多個 beanName 的處理 3. 獲取單例 bean 4. 建立單例 bean 5. bean 的例項化 6. bean 的屬性裝配 7. bean 的初始化(省略) spring-bean 的原始碼比較多,有些不影響整體分析思路的程式碼會被省略掉(程式碼中會註明),另外,想要分析所有的程式碼可能不大現實,所以,針對部分內容,我會點到為止,例如,本文只分析單例 bean 而不分析多例 bean。 # 前篇回顧 上篇部落格[Spring原始碼系列(一)--詳細介紹bean元件](https://www.cnblogs.com/ZhangZiSheng001/p/13126053.html)介紹了 bean 元件的一些重要理論概念,並通過例子演示如何使用 bean 元件。這裡回顧下,這幾個概念非常重要,是 bean 元件的理論基礎: 1. **例項化、屬性裝配和初始化的概念**。 例項化指創建出一個新的物件;屬性裝配指給物件的成員屬性賦值; 初始化指呼叫物件的初始化方法。 2. **什麼是 bean**:某個類的例項或描述物件,被註冊到了 Spring IoC 容器,這時通過 Spring IoC 容器獲取的這個類的物件就是 bean。 3. **什麼是 beanFactory**:一個工廠,用於註冊 bean 和獲取 bean。 5. **什麼是 beanDefinition**:一個描述物件,用來描述 bean 的例項化、屬性裝配、初始化等資訊。 # beanFactory的設計 從客戶端來看,一個完整的 beanFactory 工廠包含以下基本功能: 1. 註冊別名。對應下圖的`AliasRegistry`介面。 2. 註冊單例物件。對應下圖的`SingletonBeanRegistry`介面。 3. 註冊`BeanDefinition`物件。對應下圖的`BeanDefinitionRegistry`介面。 4. 獲取 bean。對應下圖的`BeanFactory`介面。 在 spring-bean 元件中,`DefaultListableBeanFactory`就是一個完整的 beanFactory 工廠,也可以說是一個 IoC 容器。 ![BeanFactoryUML_01](https://img2020.cnblogs.com/blog/1731892/202006/1731892-20200614181541484-2054058872.png) `BeanFactory`還有幾個擴充套件介面,用的比較多的可能是`ConfigurableBeanFactory`和`AutowireCapableBeanFactory`: 1. `HierarchicalBeanFactory`用於提供父子工廠的支援。例如,當前 beanFactory 找不到 bean 時,會嘗試從 parent beanFactory 中獲取。 2. `ConfigurableBeanFactory`用於提供配置 beanFactory 的支援。例如,註冊`BeanPostProcessor`、註冊 `TypeConverter`、註冊`OrderComparator`等。 3. `ListableBeanFactory`用於提供批量獲取 bean 的支援(不包含父工廠的 bean)。例如,我們可以根據型別獲取 beanName-bean 的 map。 4. `AutowireCapableBeanFactory`用於提供例項化、屬性裝配、初始化等一系列管理 bean 生命週期的支援。 例如,該介面包含了 createBean、autowireBean、initializeBean、destroyBean 等方法。 當我們註冊 bean 時,根據註冊方式的不同,bean 的註冊資訊會被放入兩個不同的地方。 ```java class DefaultSingletonBeanRegistry { // beanName=singletonObject鍵值對 // 除了registerSingleton的會放在這裡,registerBeanDefinition生成的單例bean例項也會放在這裡 private final Map singletonObjects = new ConcurrentHashMap<>(256); } class DefaultListableBeanFactory { // beanName=beanDefination鍵值對 private final Map 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 的優先順序更高一些**。 ```java public T getBean(Class requiredType) throws BeansException { // 適配入參 // 可以看到,我們獲取bean時還可以指定構造引數 return getBean(requiredType, (Object[]) null); } public T getBean(Class 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 resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) { // 這裡的NamedBeanHolder就是簡單的對bean例項封裝了一層,不用太關注 NamedBeanHolder 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,會進行以下處理: 1. 如果存在通過`registerSingleton`註冊的 beanName,或者通過`registerBeanDefinition`註冊且 `autowireCandidate = true` 的 beanName,則僅保留它們,並剔除其他的 beanName; 2. 如果還是存在多個 beanName,檢查是否存在唯一一個通過`registerBeanDefinition`且`isPrimary = true`的(存在多個會報錯),存在的話將它作為匹配到的唯一 beanName; 3. 如果還是存在多個 beanName,通過我們註冊的`OrderComparator`來確定優先值最小的作為唯一 beanName,注意,通過`registerSingleton`註冊的和通過`registerBeanDefinition`註冊的,比較的物件是不一樣的; 4. 如果還是存在多個 beanName,根據 nonUniqueAsNull,為 true 是返回 null,為 false 丟擲`NoUniqueBeanDefinitionException`異常。 ```java private NamedBeanHolder 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 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 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, Object...)`。這個方法裡包括四個步驟: 1. 轉義name。主要指的是當 name 是別名或者是 “&” + factory beanName 形式時進行轉義; 2. 如果是單例 bean 且構造引數為空,則會從 singletonObjects 中獲取已生成的 bean,或者從 earlySingletonObjects/singletonFactories 中獲取已經例項化但可能還沒裝配或初始化的 bean。如果獲取到的不是 null,直接返回對應的 bean 例項; 3. 如果當前 beanFactory 沒有指定的 beanName,則會去 parent beanFactory 中獲取; 4. 如果當前 bean 需要依賴其他 bean,則會先獲取依賴的 bean; 5. 根據 scope 選擇生成單例 bean 還是多例 bean; 6. 進行型別檢查,如果獲取的 bean 不匹配,會先用我們註冊的型別轉換器轉換,如果還是不匹配就丟擲`BeanNotOfRequiredTypeException`。 ```java public T getBean(String name, @Nullable Class requiredType, @Nullable Object... args) throws BeansException { // 適配入參 // 這裡最後一個引數指獲取的bean是否純粹用於型別檢查,如果是的話,beanFactory不會標記這個bean正在生成中,僅對單例bean有用 return doGetBean(name, requiredType, args, false); } @SuppressWarnings("unchecked") protected T doGetBean(final String name, @Nullable final Class 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)`。這個方法包括幾個過程,主要就是處理一些多執行緒問題: 2. 獲取指定 beanName 的 bean,如果已經存在,就不去建立,這時為了處理多執行緒同時建立 bean 的問題; 3. 如果當前 bean 已經在建立中,會丟擲 BeanCurrentlyInCreationException,建立單例 bean 之前是有加鎖的,按理不會出現這種情況; 4. 建立單例 bean; 6. 如果建立成功,將 bean 例項加入 singletonObjects,並且刪除掉 singletonFactories 和 earlySingletonObjects 中對應的鍵值對。 ```java 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[])`。這個方法包括以下過程: 1. 解析 beanType,並且再次包裝`RootBeanDefinition`; 3. 執行我們註冊的`InstantiationAwareBeanPostProcessor`的`postProcessBeforeInstantiation`方法,如果返回了非空物件,則將其返回。也就是說我們可以在該方法中自定義完成 bean 的例項化、裝配和初始化。 4. 建立 bean。 ```java 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[])`。這個方法主要包含以下過程: 1. 例項化 bean; 2. 執行我們註冊的`MergedBeanDefinitionPostProcessor`的`postProcessMergedBeanDefinition`方法; 3. 如果是單例,將還沒裝配和初始化的 bean 先暴露出去,即放在singletonFactories中,如果其他執行緒進來獲取,可以將這個 bean 或 factoryBean 返回,而不需要等待; 4. 屬性裝配; 5. 初始化; 6. 將生成的 bean 放入 disposableBeans 中。 ```java 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[])`。這個方法主要過程如下: 1. 解析 beanType,並對 beanType 進行一些必要的檢查; 2. 通過我們設定的 InstanceSupplier 或 FactoryMethod 來直接獲取 bean,如果有的話,直接返回該物件; 3. 如果構造引數為空,則可以複用已經解析好的構造物件(如果有的話); 4. 執行我們註冊的`SmartInstantiationAwareBeanPostProcessor`的`determineCandidateConstructors`獲取構造物件陣列; 5. 如果得到的陣列不是空,或者 beanDefination 的裝配模式為構造注入,或者 beanDefination 包含構造引數,或者我們傳入的構造引數非空,則進入例項化 bean 6. 其他情況,使用無參構造來例項化。 ```java 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 例項化時選擇構造物件的依據。 ```java public class ConstructorArgumentValues { // 索引+引數值 // 例如,對應new User(int age, String name, String address)的構造方法,可以包含元素:0=new ValueHolder(18),2=new ValueHolder("北京") private final Map indexedArgumentValues = new LinkedHashMap<>(); // 通用引數值 // 例如,對應new User(int age, String name, String address)的構造方法,如果indexedArgumentValues中不包含name的值,則可以在genericArgumentValues中查詢,我們只需要新增元素:new ValueHolder("zzs001", String.class) private final List 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`中的引數值的型別不一定和構造方法中的匹配,包括兩種情況: 1. 型別不同,但可以通過`TypeConverter`轉換的型別。例如,在`new User(int age, String name, Address address)`的構造方法中,我可以在`ConstructorArgumentValues`新增`2=new AddressVO()`,這個時候只要 spring 能找到合適的轉換器就能轉換,這個轉換過程為:**“解析未完成”(rawArguments) --》 "解析完成"(arguments)**。 2. 型別不同,引數的值指向其他 bean ,當然也可以是其他 spring 可識別的引用。例如,`new User(int age, String name, Address address)`的構造方法中,我可以在`ConstructorArgumentValues`新增`2=new RootBeanDefinition(Address.class)`,這個轉換過程為:**“未解析”(preparedArguments) --》“解析未完成”(rawArguments)**。 ```java 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 方法。 ```java 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[])`。這個方法程式碼比較多,為了更好地理解,可以分成兩種場景來看: 1. 入參裡顯式指定構造引數。這種場景的引數值預設都是解析過的,所以不需要解析,該場景要求對應的構造物件的引數數量必須和指定的一樣。 2. `BeanDefinition`物件中指定`ConstructorArgumentValues`。這種場景的引數值需要經過兩步轉換,該場景要求對應的構造物件的引數數量不小於指定的數量。 ```java 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> ambiguousConstructors = null; LinkedList 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)`。這個方法包括以下過程: 1. 執行我們註冊的`InstantiationAwareBeanPostProcessor`的`postProcessAfterInstantiation`方法,如果返回了 false,則不進行屬性裝配,直接返回; 2. 獲取 beanDefinition 中的`PropertyValues`物件,根據 beanDefinition 設定的注入型別,填充`PropertyValues`物件; 3. 執行我們註冊的`InstantiationAwareBeanPostProcessor`的`postProcessProperties`方法,可以對`PropertyValues`物件進行修改; 4. 依賴檢查(如果設定了); 5. 進行屬性裝配。 ```java 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`這個類來展開例子。 ```java public class User { private String name; private int age; private Address address; private List hobbies; } class Address { private String region; private String desc; } ``` ### propertyName的幾種形式 當我們給 beanDefinition設定屬性值時,一般都會這樣採用這樣的賦值,這裡成為“普通形式”。 ```java 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 還支援其他的賦值方式,如下,分別成為“巢狀物件形式”和“索引形式”: ```java // 巢狀物件形式 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`物件。 ```java public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyAccessor { private Map nestedPropertyAccessors; } ``` 在上面的例子中, ```java rootBeanDefinition.getPropertyValues().add("name", "zzs001"); rootBeanDefinition.getPropertyValues().add("age", 18); ``` 這種形式共用一個綁定了`User`型別例項的`PropertyAccessor`物件。 ```java // 巢狀物件形式 rootBeanDefinition.getPropertyValues().add("address.region", ""); rootBeanDefinition.getPropertyValues().add("address.desc", ""); ``` 這種形式共用一個綁定了`Address`型別例項的`PropertyAccessor`物件,該物件和"address"這個名字關聯起來維護在 nestedPropertyAccessors 中。 ```java // 索引形式 rootBeanDefinition.getPropertyValues().add("hobbies[0]", ""); ``` 這種形式也是一個綁定了`User`型別例項的`PropertyAccessor`物件,該物件和"hobbies"這個名字關聯起來維護在 nestedPropertyAccessors 中。 ### PropertyTokenHolder `PropertyTokenHolder`是`AbstractNestablePropertyAccessor`的內部類,它更多的是針對“索引形式”的 propertyName。例如,"hobbies[0]"對於的`PropertyTokenHolder`中,actualName = hobbies,canonicalName = [0],keys = {0}。 ```java 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)`方法。和構造引數一樣,設定成員屬性的引數也需要經過“兩次轉換”,這裡就不詳細講解。這個方法主要包括以下過程: 1. 獲取屬性物件列表,如果這個列表的屬性物件都已經完成“兩次轉換”,則直接裝配屬性; 2. 遍歷屬性物件列表,分別進行兩次轉換,如果列表中沒有類似`BeanDefinition`、`BeanDefinitionHolder`等的物件,則設定`PropertyValues`物件已經轉換完成,下次呼叫這個方法不用再進行轉換; 3. 屬性裝配。 ```java protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { // 如果沒有需要注入的屬性,直接返回 if (pvs.isEmpty()) { return; } // 省略部分程式碼······ MutablePropertyValues mpvs = null; // 獲取屬性物件列表 List 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 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)`方法。這裡遍歷屬性物件列表,逐個進賦值操作。 ```java 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 propertyValues = (pvs instanceof MutablePropertyValues ? ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues())); for (PropertyValue pv : propertyValues) { // 省略try-catch的程式碼和其他異常相關的程式碼······ setPropertyValue(pv); } } ``` ## setPropertyValue 進入`AbstractNestablePropertyAccessor.setPropertyValue(PropertyValue)`。這個方法包括以下過程: 1. 獲取 propertyName 對應的`PropertyAccessor`物件,這裡將解析“巢狀物件形式”的 propertyName; 2. 建立`PropertyTokenHolder`物件,這裡將解析“索引形式”的 propertyName; 3. 使用`PropertyAccessor`物件進行賦值操作。 ```java 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 是否為“索引形式”呼叫不同的方法。 ```java protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException { if (tokens.keys != null) { processKeyedProperty(tokens, pv); } else { processLocalProperty(tokens, pv); } } ``` 這裡我們不看 propertyName 為“索引形式”的方法,只看`processLocalProperty`。 ```java 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 的過程,本文未展開的內容包括: 1. 獲取和建立多例 bean; 2. 無參構造例項化; 3. 屬性裝配中,屬性值列表的填充(autowireByName和autowireByType)、屬性名為索引形式的屬性裝配 4. bean 的初始化。 感興趣的讀者可以自行分析。另外,以上內容如有錯誤,歡迎指正。 最後,感謝閱讀。 > 相關原始碼請移步:[ spring-beans](https://github.com/ZhangZiSheng001/spring-projects/tree/master/spring-beans) > 本文為原創文章,轉載請附上原文出處連結:[https://www.cnblogs.com/ZhangZiSheng001/p/13196228.html](https://www.cnblogs.com/ZhangZiSheng001/p/1319622