一.簡介
在這篇文章中,我們將會了解到Spring是如何將配置文件中的屬性值填充到bean對象中。對於填充屬性這個過程,簡單點來說,JavaBean 的每個屬性通常都有 getter/setter 方法,我們可以直接調用 setter 方法將屬性值設置進去。當然,這樣做還是太簡單了,填充屬性的過程中還有許多事情要做。比如在 Spring 配置中,所有屬性值都是以字符串的形式進行配置的,我們在將這些屬性值賦值給對象的成員變量時,要根據變量類型進行相應的類型轉換。對於一些集合類的配置,比如、和,還要將這些配置轉換成相應的集合對象才能進行後續的操作。除此之外,如果用戶配置了自動注入(autowire = byName/byType),Spring 還要去爲自動注入的屬性尋找合適的注入項。由此可以見,屬性填充的整個過程還是很複雜的,並非是簡單調用 setter 方法設置屬性值即可。
接下來,我們深入到源碼中,從源碼中瞭解屬性填充的整個過程。
二.源碼分析
2.1 populateBean源碼
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
// 獲取屬性列表
PropertyValues pvs = mbd.getPropertyValues();
if (bw == null) {
if (!pvs.isEmpty()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
return;
}
}
boolean continueWithPropertyPopulation = true;
/*
* 在屬性被填充前,給 InstantiationAwareBeanPostProcessor 類型的後置處理器一個修改
* bean 狀態的機會。關於這段後置引用,官方的解釋是:讓用戶可以自定義屬性注入。比如用戶實現一
* 個 InstantiationAwareBeanPostProcessor 類型的後置處理器,並通過
* postProcessAfterInstantiation 方法向 bean 的成員變量注入自定義的信息。當然,如果無
* 特殊需求,直接使用配置中的信息注入即可。另外,Spring 並不建議大家直接實現
* InstantiationAwareBeanPostProcessor 接口,如果想實現這種類型的後置處理器,更建議
* 通過繼承 InstantiationAwareBeanPostProcessorAdapter 抽象類實現自定義後置處理器。
*/
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
/*
* 如果上面設置 continueWithPropertyPopulation = false,表明用戶可能已經自己填充了
* bean 的屬性,不需要 Spring 幫忙填充了。此時直接返回即可
*/
if (!continueWithPropertyPopulation) {
return;
}
// 根據名稱或類型注入依賴
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// 通過屬性名稱注入依賴
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// 通過屬性類型注入依賴
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
/*
* 這裏又是一種後置處理,用於在 Spring 填充屬性到 bean 對象前,對屬性的值進行相應的處理,
* 比如可以修改某些屬性的值。這時注入到 bean 中的值就不是配置文件中的內容了,
* 而是經過後置處理器修改後的內容
*/
if (hasInstAwareBpps || needsDepCheck) {
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 對屬性進行後置處理
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}
// 應用屬性值到 bean 對象中
applyPropertyValues(beanName, mbd, bw, pvs);
}
在populateBean函數中提供了這樣的處理流程:
- 獲取屬性列表 pvs
- 在屬性被填充到 bean 前,應用後置處理自定義屬性填充。InstantiationAwareBeanPostProcessor處理器的postProcessAfterInstantiation函數的應用,此函數可以控制程序是否繼續進行屬性填充。
- 根據注入類型(byName/byType),提取依賴的bean,並統一存入PropertyValues中。
- 應用InstantiationAwareBeanPostProcessor處理器的postProcessPropertyValues方法,對屬性獲取完畢填充前對屬性的再次處理。
- 將所有的PropertyValues中的屬性填充至BeanWrapper中。
注意第3步,也就是根據名稱或類型解析相關依賴(autowire)。該邏輯只會解析依賴,並不會將解析出的依賴立即注入到 bean 對象中。所有的屬性值是在 applyPropertyValues 方法中統一被注入到 bean 對象中的。
下來對依賴注入(autowireByName/autowireByType)以及屬性填充進一步分析實現細節。
2.2 autowireByName 方法分析
protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
/*
* 獲取非簡單類型屬性的名稱,且該屬性未被配置在配置文件中。這裏從反面解釋一下什麼是"非簡單類型"
* 屬性,我們先來看看 Spring 認爲的"簡單類型"屬性有哪些,如下:
* 1. CharSequence 接口的實現類,比如 String
* 2. Enum
* 3. Date
* 4. URI/URL
* 5. Number 的繼承類,比如 Integer/Long
* 6. byte/short/int... 等基本類型
* 7. Locale
* 8. 以上所有類型的數組形式,比如 String[]、Date[]、int[] 等等
*
* 除了要求非簡單類型的屬性外,還要求屬性未在配置文件中配置過,也就是 pvs.contains(pd.getName()) = false。
*/
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
// 檢測是否存在與 propertyName 相關的 bean 或 BeanDefinition。若存在,則調用 BeanFactory.getBean 方法獲取 bean 實例
if (containsBean(propertyName)) {
// 從容器中獲取相應的 bean 實例
Object bean = getBean(propertyName);
// 將解析出的 bean 存入到屬性值列表(pvs)中
pvs.add(propertyName, bean);
registerDependentBean(propertyName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Added autowiring by name from bean name '" + beanName +
"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
"' by name: no matching bean found");
}
}
}
}
這個方法的邏輯比較簡單,該方法首先獲取非簡單類型屬性的名稱,然後再根據名稱到容器中獲得對應的bean實例,最後再將獲取的bean添加到屬性列表中。
2.3 autowireByType 方法分析
相比較autowireByName,autowireByType 則要複雜一些,複雜之處在於解析依賴的過程。我們複雜的地方當做一個黑盒,只需關注這個黑盒實現的功能即可。
protected void autowireByType(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
Set<String> autowiredBeanNames = new LinkedHashSet<String>(4);
// 獲取非簡單類型的屬性
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
try {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
// 如果屬性類型爲 Object,則忽略,不做解析
if (Object.class != pd.getPropertyType()) {
/*
* 獲取 setter 方法(write method)的參數信息,比如參數在參數列表中的
* 位置,參數類型,以及該參數所歸屬的方法等信息
*/
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
// Do not allow eager init for type matching in case of a prioritized post-processor.
boolean eager = !PriorityOrdered.class.isAssignableFrom(bw.getWrappedClass());
// 創建依賴描述對象
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
/*
* 下面的方法用於解析依賴。過程比較複雜,先把這裏看成一個黑盒,我們只要知道這
* 個方法可以幫我們解析出合適的依賴即可。
*/
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if (autowiredArgument != null) {
// 將解析出的 bean 存入到屬性值列表(pvs)中
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
propertyName + "' to bean named '" + autowiredBeanName + "'");
}
}
autowiredBeanNames.clear();
}
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
}
}
}
autowireByType首先也是獲得非簡單類型屬性的名稱,然後再根據屬性名稱獲得屬性描述符,並由屬性描述符獲取方法參數對象MethodParameter,隨後在根據MethodParameter對象獲取依賴描述符對象,整個過程爲beanName->PropertyDescriptor->MethodParameter->DependencyDescriptor。在獲取到依賴描述符對象後,再根據依賴描述符對象解析出合適的依賴,最後將解析出的結果存入屬性列表pvs中即可。
2.4 解析依賴
2.4.1 resolveDependency
resolveDependency 依賴查找解決了以下場景:
- Optional:JDK8 提供了 API。主要是將依賴設置非強制依賴,即 descriptor.required=false。
- 延遲依賴注入支持:ObjectFactory、ObjectProvider、javax.inject.Provider 沒有本質的區別。
- 另一種延遲注入的支持 - @Lazy 屬性。
- 根據類型查找依賴 - doResolveDependency。
public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
if (javaUtilOptionalClass == descriptor.getDependencyType()) {
return new OptionalDependencyFactory().createOptionalDependency(descriptor, requestingBeanName);
}
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330ProviderFactory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
// 解析依賴
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
2.4.2 doResolveDependency
doResolveDependency 封裝了依賴查找的各種情況:
- 快速查找: @Autowired 註解處理場景。AutowiredAnnotationBeanPostProcessor 處理 @Autowired 註解時,如果注入的對象只有一個,會將該 bean 對應的名稱緩存起來,下次直接通過名稱查找會快很多。
- 注入指定值:@Value 註解處理場景。QualifierAnnotationAutowireCandidateResolver 處理 @Value 註解時,會讀取 @Value 對應的值進行注入。如果是 String 要經過三個過程:①佔位符處理 -> ②EL 表達式解析 -> ③類型轉換,這也是一般的處理過程,BeanDefinitionValueResolver 處理 String 對象也是這個過程。
- 集合依賴查詢:直接全部委託給 resolveMultipleBeans 方法。
- 單個依賴查詢:先調用 findAutowireCandidates 查找所有可用的依賴,如果有多個依賴,則根據規則匹配: @Primary -> @Priority -> ③方法名稱或字段名稱。
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
// 該方法最終調用了 beanFactory.getBean(String, Class),從容器中獲取依賴
Object shortcut = descriptor.resolveShortcut(this);
// 如果容器中存在所需依賴,這裏進行斷路操作,提前結束依賴解析邏輯
if (shortcut != null) {
return shortcut;
}
Class<?> type = descriptor.getDependencyType();
// 處理 @value 註解
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String) {
String strVal = resolveEmbeddedValue((String) value);
BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
value = evaluateBeanDefinitionString(strVal, bd);
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
// 解析數組、list、map 等類型的依賴
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
/*
* 按類型查找候選列表,如果某個類型已經被實例化,則返回相應的實例。
* 比如下面的配置:
*
* <bean name="mongoDao" class="xyz.coolblog.autowire.MongoDao" primary="true"/>
* <bean name="service" class="xyz.coolblog.autowire.Service" autowire="byType"/>
* <bean name="mysqlDao" class="xyz.coolblog.autowire.MySqlDao"/>
*
* MongoDao 和 MySqlDao 均實現自 Dao 接口,Service 對象(不是接口)中有一個 Dao
* 類型的屬性。現在根據類型自動注入 Dao 的實現類。這裏有兩個候選 bean,一個是
* mongoDao,另一個是 mysqlDao,其中 mongoDao 在 service 之前實例化,
* mysqlDao 在 service 之後實例化。此時 findAutowireCandidates 方法會返回如下的結果:
*
* matchingBeans = [ <mongoDao, Object@MongoDao>, <mysqlDao, Class@MySqlDao> ]
*
* 注意 mysqlDao 還未實例化,所以返回的是 MySqlDao.class。
*
* findAutowireCandidates 這個方法邏輯比較複雜,我簡單說一下它的工作流程吧,如下:
* 1. 從 BeanFactory 中獲取某種類型 bean 的名稱,比如上面的配置中
* mongoDao 和 mysqlDao 均實現了 Dao 接口,所以他們是同一種類型的 bean。
* 2. 遍歷上一步得到的名稱列表,並判斷 bean 名稱對應的 bean 是否是合適的候選項,
* 若合適則添加到候選列表中,並在最後返回候選列表
*
*/
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
// 拋出 NoSuchBeanDefinitionException 異常
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
if (matchingBeans.size() > 1) {
/*
* matchingBeans.size() > 1,則表明存在多個可注入的候選項,這裏判斷使用哪一個
* 候選項。比如下面的配置:
*
* <bean name="mongoDao" class="xyz.coolblog.autowire.MongoDao" primary="true"/>
* <bean name="mysqlDao" class="xyz.coolblog.autowire.MySqlDao"/>
*
* mongoDao 的配置中存在 primary 屬性,所以 mongoDao 會被選爲最終的候選項。如
* 果兩個 bean 配置都沒有 primary 屬性,則需要根據優先級選擇候選項。優先級這一塊
* 的邏輯沒細看,不多說了。
*/
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
// 拋出 NoUniqueBeanDefinitionException 異常
return descriptor.resolveNotUnique(type, matchingBeans);
}
else {
return null;
}
}
// 根據解析出的 autowiredBeanName,獲取相應的候選項
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else { // 只有一個候選項,直接取出來即可
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
// 返回候選項實例,如果實例是 Class 類型,則調用 beanFactory.getBean(String, Class) 獲取相應的 bean。否則直接返回即可
return (instanceCandidate instanceof Class ?
descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate);
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
總結一下 doResolveDependency 的執行流程吧,如下:
- 首先將 beanName 和 requiredType 作爲參數,並嘗試從 BeanFactory 中獲取與此對於的 bean。若獲取成功,就可以提前結束 doResolveDependency 的邏輯。
- 處理 @value 註解
- 解析數組、List、Map 等類型的依賴,如果解析結果不爲空,則返回結果
- 根據類型查找合適的候選項
- 如果候選項的數量爲0,則拋出異常。爲1,直接從候選列表中取出即可。若候選項數量 > 1,則在多個候選項中確定最優候選項,若無法確定則拋出異常
- 若候選項是 Class 類型,表明候選項還沒實例化,此時通過 BeanFactory.getBean 方法對其進行實例化。若候選項是非 Class 類型,則表明已經完成了實例化,此時直接返回即可。
doResolveDependency 方法的四個功能,快速查找和集合處理都委託給了其它方法,注入指定值雖然看起來複雜,但佔位符處理、EL 表達式解析、類型轉換這三個功能點都有具體的類處理,也不是本文的重點。
重點看一下單個依賴的查詢,弄明白了單個依賴的查詢,其它集合依賴也差不多。
- 查找容器中所有可用依賴:findAutowireCandidates 方法根據類型查找依賴。
- 如何有多個依賴怎麼處理?其實 Spring 有一套通用的流程,先按 @Primary 查找,再按 @Priority,最後按方法名稱或字段名稱查找,直到只有一個 bean 爲止。相關的匹配規則見 determineAutowireCandidate 方法。
- 此時只有一個依賴,從容器獲取真實的 bean。descriptor.resolveCandidate 方法根據名稱 autowiredBeanName 實例化對象。
思考:findAutowireCandidates 返回的爲什麼是對象類型,而不是實例對象?
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
matchingBeans 中的 Object 對象可能是對象類型,而不全部是實例對象。因爲 findAutowireCandidates 方法是根據類型 type 查找名稱 beanNames,如果容器中該 beanName 還沒有實例化,findAutowireCandidates 不會畫蛇添足直接實例化該 bean,當然如果已經實例化了會直接返回這個 bean。
2.4.3 findAutowireCandidates
根據上面的分析,resolveDependency 方法對 Optional、延遲注入、懶加載注入等分別進行了處理。之後 doResolveDependency 在正式查找之前看能不能快速查找,如緩存 beanName、@Value 等快速指定需要注入的值,避免通過類型查找,最後纔對集合依賴和單一依賴分別進行了處理。實際上,無論是集合依賴還是單一依賴查找,本質上都是調用 findAutowireCandidates 進行類型依賴查找。
protected Map<String, Object> findAutowireCandidates(
@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
// 1. Spring IoC 內部依賴 resolvableDependencies
for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
Class<?> autowiringType = classObjectEntry.getKey();
if (autowiringType.isAssignableFrom(requiredType)) {
Object autowiringValue = classObjectEntry.getValue();
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}
// 2. 類型查找:本質上遞歸調用beanFactory#beanNamesForType。先匹配實例類型,再匹配bd。
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
for (String candidate : candidateNames) {
// 2.1 isSelfReference說明beanName和candidate本質是同一個對象
// isAutowireCandidate進一步匹配bd.autowireCandidate、泛型、@@Qualifier等進行過濾
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
// 2.2 添加到候選對象中
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
// 3. 補償機制:如果依賴查找無法匹配,怎麼辦?包含泛型補償和自身引用補償兩種。
if (result.isEmpty()) {
boolean multiple = indicatesMultipleBeans(requiredType);
// 3.1 fallbackDescriptor: 泛型補償,實際上是允許注入對象類型的泛型存在無法解析的情況
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
// 3.2 補償1:不允許自稱依賴,但如果是集合依賴,需要過濾非@Qualifier對象。什麼場景?
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
// 3.3 補償2:允許自稱依賴,但如果是集合依賴,注入的集合依賴中需要過濾自己
if (result.isEmpty() && !multiple) {
for (String candidate : candidateNames) {
if (isSelfReference(beanName, candidate) &&
(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
isAutowireCandidate(candidate, fallbackDescriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
}
}
return result;
}
findAutowireCandidates 大致可以分爲三步:先查找內部依賴,再根據類型查找,最後沒有可注入的依賴則進行補償。
- 查找內部依賴:Spring IoC 容器本身相關依賴,這部分內容是用戶而言是透明的,也不用感知。resolvableDependencies 集合中註冊如 BeanFactory、ApplicationContext 、ResourceLoader、ApplicationEventPublisher 等。
- 根據類型查找:包括 ①外部託管 Bean ②註冊 BeanDefinition。類型查找調用 beanFactory#beanNamesForType 方法。
- 自身引用:isSelfReference 方法判斷 beanName 和 candidate 是否是同一個對象,包括兩種情況:一是名稱完全相同,二是 candidate 對應的工廠對象創建了 beanName。
- 是否可以注入:底層實際調用 resolver.isAutowireCandidate 方法進行過濾,包含三重規則:①bd.autowireCandidate=true -> ②泛型匹配 -> ③@Qualifier。
- 補償機制:如果依賴查找無法匹配,怎麼辦?Spring 提供了兩種補償機制:一是泛型補償,允許注入對象對象的泛型無法解析,二是自身引用補償,對這兩種機制使用如下:
- 先使用泛型補償,不允許自身引用:即 fallbackDescriptor。此時如果是集合依賴,對象必須是 @Qualifier 類型。
- 允許泛型補償和自身引用補償:但如果是集合依賴,必須過濾自己本身,即 beanName.equals(candidate) 必須剔除。
這個方法中調用的方法沒有詳細展開,想要了解的可以自己看看。
2.5 applyPropertyValues 方法分析
程序運行到這裏,已經完成了對所有注入屬性的獲取,但是獲取的屬性是以PropertyValues形式存在的,還並沒有應用到已經實例化的bean中,這一個工作是在applyPropertyValues方法中。
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
if (pvs == null || pvs.isEmpty()) {
return;
}
if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
}
MutablePropertyValues mpvs = null;
List<PropertyValue> original;
//檢測屬性列表是否已經轉換過了,若轉換了,直接填充屬性
if (pvs instanceof MutablePropertyValues) {
mpvs = (MutablePropertyValues) pvs;
// 如果屬性列表 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,該Bean用於將bean定義對象中包含的值解析爲應用於目標bean實例的實際值。
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// 用於存放實際解析後的屬性集合
List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());
boolean resolveNecessary = false;
// 遍歷屬性列表,解析原始值originalValue得到resolvedValue
for (PropertyValue pv : original) {
// 如果屬性值被轉換過,則就不需要再次轉換
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {
// 若該屬性沒有被解析過
String propertyName = pv.getName(); // 屬性名稱
Object originalValue = pv.getValue(); // 屬性未經類型轉換的值
/*
* 解析屬性值。舉例說明,先看下面的配置:
*
* <bean id="macbook" class="MacBookPro">
* <property name="manufacturer" value="Apple"/>
* <property name="width" value="280"/>
* <property name="cpu" ref="cpu"/>
* <property name="interface">
* <list>
* <value>USB</value>
* <value>HDMI</value>
* <value>Thunderbolt</value>
* </list>
* </property>
* </bean>
*
* 上面是一款電腦的配置信息,每個 property 配置經過下面的方法解析後,返回如下結果:
* propertyName = "manufacturer", resolvedValue = "Apple"
* propertyName = "width", resolvedValue = "280"
* propertyName = "cpu", resolvedValue = "CPU@1234" 注:resolvedValue 是一個對象
* propertyName = "interface", resolvedValue = ["USB", "HDMI", "Thunderbolt"]
*
* 如上所示,resolveValueIfNecessary 會將 ref 解析爲具體的對象,將 <list>
* 標籤轉換爲 List 對象等。對於 int 類型的配置,這裏並未做轉換,所以
* width = "280",還是字符串。除了解析上面幾種類型,該方法還會解析 <set/>、
* <map/>、<array/> 等集合配置
*/
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
/*
* convertible 表示屬性值是否可轉換,由兩個條件合成而來。第一個條件不難理解,解釋
* 一下第二個條件。第二個條件用於檢測 propertyName 是否是 nested 或者 indexed,
* 直接舉例說明吧:
*
* public class Room {
* private Door door = new Door();
* }
*
* room 對象裏面包含了 door 對象,如果我們想向 door 對象中注入屬性值,則可以這樣配置:
*
* <bean id="room" class="xyz.coolblog.Room">
* <property name="door.width" value="123"/>
* </bean>
*
* isNestedOrIndexedProperty 會根據 propertyName 中是否包含 . 或 [ 返回
* true 和 false。包含則返回 true,否則返回 false。
* 關於 nested 類型的屬性,大家還可以參考 Spring 的官方文檔:
* https://docs.spring.io/spring/docs/4.3.17.RELEASE/spring-framework-reference/htmlsingle/#beans-beans-conventions
*/
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
// 對於一般的屬性,convertible 通常爲 true
if (convertible) {
// 對屬性值的類型進行轉換,比如將 String 類型的屬性值 "123" 轉爲 Integer 類型的 123
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
/*
* 如果 originalValue 是通過 autowireByType 或 autowireByName 解析而來,
* 那麼此處條件成立,即 (resolvedValue == originalValue) = true
*/
if (resolvedValue == originalValue) {
if (convertible) {
// 可能將轉換後的值存儲在merged bean definition中,避免對每個創建的bean實例進行重新轉換。
pv.setConvertedValue(convertedValue);
}
deepCopy.add(pv);
}
/*
* 如果原始值 originalValue 是 TypedStringValue,且轉換後的值
* convertedValue 不是 Collection 或數組類型,則將轉換後的值存入到 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));
}
}
}
if (mpvs != null && !resolveNecessary) {
mpvs.setConverted();
}
try {
// 將所有的屬性值設置到 bean 實例中
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
這個方法的執行流程如下:
- 檢測屬性值列表是否已轉換過的,若轉換過,則直接填充屬性,無需再次轉換
- 遍歷屬性值列表 pvs,解析原始值 originalValue,得到解析值 resolvedValue
- 對解析後的屬性值 resolvedValue 進行類型轉換
- 將類型轉換後的屬性值設置到 PropertyValue 對象中,並將 PropertyValue 對象存入 deepCopy 集合中
- 將 deepCopy 中的屬性信息注入到 bean 對象中
Spring IoC 依賴注入(三)resolveDependency
《Spring源碼深度解析》