摘要:源碼分析源碼一覽本節(jié),我們先來看一下填充屬性的方法,即。所有的屬性值是在方法中統(tǒng)一被注入到對(duì)象中的。檢測是否存在與相關(guān)的或。這樣可以在很大程度上降低源碼分析的難度。若候選項(xiàng)是非類型,則表明已經(jīng)完成了實(shí)例化,此時(shí)直接返回即可。
1. 簡介
本篇文章,我們來一起了解一下 Spring 是如何將配置文件中的屬性值填充到 bean 對(duì)象中的。我在前面幾篇文章中介紹過 Spring 創(chuàng)建 bean 的流程,即 Spring 先通過反射創(chuàng)建一個(gè)原始的 bean 對(duì)象,然后再向這個(gè)原始的 bean 對(duì)象中填充屬性。對(duì)于填充屬性這個(gè)過程,簡單點(diǎn)來說,JavaBean 的每個(gè)屬性通常都有 getter/setter 方法,我們可以直接調(diào)用 setter 方法將屬性值設(shè)置進(jìn)去。當(dāng)然,這樣做還是太簡單了,填充屬性的過程中還有許多事情要做。比如在 Spring 配置中,所有屬性值都是以字符串的形式進(jìn)行配置的,我們?cè)趯⑦@些屬性值賦值給對(duì)象的成員變量時(shí),要根據(jù)變量類型進(jìn)行相應(yīng)的類型轉(zhuǎn)換。對(duì)于一些集合類的配置,比如 、
關(guān)于屬性填充的一些知識(shí),本章先介紹這里。接下來,我們深入到源碼中,從源碼中了解屬性填充的整個(gè)過程。
2. 源碼分析 2.1 populateBean 源碼一覽本節(jié),我們先來看一下填充屬性的方法,即 populateBean。該方法并不復(fù)雜,但它所調(diào)用的一些方法比較復(fù)雜。不過好在我們這里只需要知道這些方法都有什么用就行了,暫時(shí)不用糾結(jié)細(xì)節(jié)。好了,下面看源碼吧。
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 類型的后置處理器一個(gè)修改 * bean 狀態(tài)的機(jī)會(huì)。關(guān)于這段后置引用,官方的解釋是:讓用戶可以自定義屬性注入。比如用戶實(shí)現(xiàn)一 * 個(gè) InstantiationAwareBeanPostProcessor 類型的后置處理器,并通過 * postProcessAfterInstantiation 方法向 bean 的成員變量注入自定義的信息。當(dāng)然,如果無 * 特殊需求,直接使用配置中的信息注入即可。另外,Spring 并不建議大家直接實(shí)現(xiàn) * InstantiationAwareBeanPostProcessor 接口,如果想實(shí)現(xiàn)這種類型的后置處理器,更建議 * 通過繼承 InstantiationAwareBeanPostProcessorAdapter 抽象類實(shí)現(xiàn)自定義后置處理器。 */ 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; } } } } /* * 如果上面設(shè)置 continueWithPropertyPopulation = false,表明用戶可能已經(jīng)自己填充了 * bean 的屬性,不需要 Spring 幫忙填充了。此時(shí)直接返回即可 */ if (!continueWithPropertyPopulation) { return; } // 根據(jù)名稱或類型注入依賴 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 對(duì)象前,對(duì)屬性的值進(jìn)行相應(yīng)的處理, * 比如可以修改某些屬性的值。這時(shí)注入到 bean 中的值就不是配置文件中的內(nèi)容了, * 而是經(jīng)過后置處理器修改后的內(nèi)容 */ if (hasInstAwareBpps || needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); if (hasInstAwareBpps) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; // 對(duì)屬性進(jìn)行后置處理 pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } } } if (needsDepCheck) { checkDependencies(beanName, mbd, filteredPds, pvs); } } // 應(yīng)用屬性值到 bean 對(duì)象中 applyPropertyValues(beanName, mbd, bw, pvs); }
上面的源碼注釋的比較詳細(xì)了,下面我們來總結(jié)一下這個(gè)方法的執(zhí)行流程。如下:
獲取屬性列表 pvs
在屬性被填充到 bean 前,應(yīng)用后置處理自定義屬性填充
根據(jù)名稱或類型解析相關(guān)依賴
再次應(yīng)用后置處理,用于動(dòng)態(tài)修改屬性列表 pvs 的內(nèi)容
將屬性應(yīng)用到 bean 對(duì)象中
注意第3步,也就是根據(jù)名稱或類型解析相關(guān)依賴(autowire)。該邏輯只會(huì)解析依賴,并不會(huì)將解析出的依賴立即注入到 bean 對(duì)象中。所有的屬性值是在 applyPropertyValues 方法中統(tǒng)一被注入到 bean 對(duì)象中的。
在下面的章節(jié)中,我將會(huì)對(duì) populateBean 方法中比較重要的幾個(gè)方法調(diào)用進(jìn)行分析,也就是第3步和第5步中的三個(gè)方法。好了,本節(jié)先到這里。
2.2 autowireByName 方法分析本節(jié)來分析一下 autowireByName 方法的代碼,其實(shí)這個(gè)方法根據(jù)方法名,大家應(yīng)該知道它有什么用了。所以我也就不啰嗦了,咱們直奔主題,直接分析源碼:
protected void autowireByName( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { /* * 獲取非簡單類型屬性的名稱,且該屬性未被配置在配置文件中。這里從反面解釋一下什么是"非簡單類型" * 屬性,我們先來看看 Spring 認(rèn)為的"簡單類型"屬性有哪些,如下: * 1. CharSequence 接口的實(shí)現(xiàn)類,比如 String * 2. Enum * 3. Date * 4. URI/URL * 5. Number 的繼承類,比如 Integer/Long * 6. byte/short/int... 等基本類型 * 7. Locale * 8. 以上所有類型的數(shù)組形式,比如 String[]、Date[]、int[] 等等 * * 除了要求非簡單類型的屬性外,還要求屬性未在配置文件中配置過,也就是 pvs.contains(pd.getName()) = false。 */ String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { // 檢測是否存在與 propertyName 相關(guān)的 bean 或 BeanDefinition。若存在,則調(diào)用 BeanFactory.getBean 方法獲取 bean 實(shí)例 if (containsBean(propertyName)) { // 從容器中獲取相應(yīng)的 bean 實(shí)例 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"); } } } }
autowireByName 方法的邏輯比較簡單,該方法首先獲取非簡單類型屬性的名稱,然后再根據(jù)名稱到容器中獲取相應(yīng)的 bean 實(shí)例,最后再將獲取到的 bean 添加到屬性列表中即可。既然這個(gè)方法比較簡單,那我也就不多說了,繼續(xù)下面的分析。
2.3 autowireByType 方法分析本節(jié)我們來分析一下 autowireByName 的孿生兄弟 autowireByType,相較于 autowireByName,autowireByType 則要復(fù)雜一些,復(fù)雜之處在于解析依賴的過程。不過也沒關(guān)系,如果我們不過于糾結(jié)細(xì)節(jié),我們完全可以把一些復(fù)雜的地方當(dāng)做一個(gè)黑盒,我們只需要要知道這個(gè)黑盒有什么用即可。這樣可以在很大程度上降低源碼分析的難度。好了,其他的就不多說了,咱們來分析源碼吧。
protected void autowireByType( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } SetautowiredBeanNames = new LinkedHashSet (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)的參數(shù)信息,比如參數(shù)在參數(shù)列表中的 * 位置,參數(shù)類型,以及該參數(shù)所歸屬的方法等信息 */ 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()); // 創(chuàng)建依賴描述對(duì)象 DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); /* * 下面的方法用于解析依賴。過程比較復(fù)雜,先把這里看成一個(gè)黑盒,我們只要知道這 * 個(gè)方法可以幫我們解析出合適的依賴即可。 */ 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 的代碼本身并不復(fù)雜。和 autowireByName 一樣,autowireByType 首先也是獲取非簡單類型屬性的名稱。然后再根據(jù)屬性名獲取屬性描述符,并由屬性描述符獲取方法參數(shù)對(duì)象 MethodParameter,隨后再根據(jù) MethodParameter 對(duì)象獲取依賴描述符對(duì)象,整個(gè)過程為 beanName → PropertyDescriptor → MethodParameter → DependencyDescriptor。在獲取到依賴描述符對(duì)象后,再根據(jù)依賴描述符解析出合適的依賴。最后將解析出的結(jié)果存入屬性列表 pvs 中即可。
關(guān)于 autowireByType 方法中出現(xiàn)的幾種描述符對(duì)象,大家自己去看一下他們的實(shí)現(xiàn)吧,我就不分析了。接下來,我們來分析一下解析依賴的方法 resolveDependency。如下:
public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, SetautowiredBeanNames, 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; } } public Object doResolveDependency(DependencyDescriptor descriptor, String beanName, Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException { InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); try { // 該方法最終調(diào)用了 beanFactory.getBean(String, Class),從容器中獲取依賴 Object shortcut = descriptor.resolveShortcut(this); // 如果容器中存在所需依賴,這里進(jìn)行斷路操作,提前結(jié)束依賴解析邏輯 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())); } // 解析數(shù)組、list、map 等類型的依賴 Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); if (multipleBeans != null) { return multipleBeans; } /* * 按類型查找候選列表,如果某個(gè)類型已經(jīng)被實(shí)例化,則返回相應(yīng)的實(shí)例。 * 比如下面的配置: * * * * * * MongoDao 和 MySqlDao 均實(shí)現(xiàn)自 Dao 接口,Service 對(duì)象(不是接口)中有一個(gè) Dao * 類型的屬性?,F(xiàn)在根據(jù)類型自動(dòng)注入 Dao 的實(shí)現(xiàn)類。這里有兩個(gè)候選 bean,一個(gè)是 * mongoDao,另一個(gè)是 mysqlDao,其中 mongoDao 在 service 之前實(shí)例化, * mysqlDao 在 service 之后實(shí)例化。此時(shí) findAutowireCandidates 方法會(huì)返回如下的結(jié)果: * * matchingBeans = [ , ] * * 注意 mysqlDao 還未實(shí)例化,所以返回的是 MySqlDao.class。 * * findAutowireCandidates 這個(gè)方法邏輯比較復(fù)雜,我簡單說一下它的工作流程吧,如下: * 1. 從 BeanFactory 中獲取某種類型 bean 的名稱,比如上面的配置中 * mongoDao 和 mysqlDao 均實(shí)現(xiàn)了 Dao 接口,所以他們是同一種類型的 bean。 * 2. 遍歷上一步得到的名稱列表,并判斷 bean 名稱對(duì)應(yīng)的 bean 是否是合適的候選項(xiàng), * 若合適則添加到候選列表中,并在最后返回候選列表 * * findAutowireCandidates 比較復(fù)雜,我并未完全搞懂,就不深入分析了。見諒 */ Map 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,則表明存在多個(gè)可注入的候選項(xiàng),這里判斷使用哪一個(gè) * 候選項(xiàng)。比如下面的配置: * * * * * mongoDao 的配置中存在 primary 屬性,所以 mongoDao 會(huì)被選為最終的候選項(xiàng)。如 * 果兩個(gè) bean 配置都沒有 primary 屬性,則需要根據(jù)優(yōu)先級(jí)選擇候選項(xiàng)。優(yōu)先級(jí)這一塊 * 的邏輯沒細(xì)看,不多說了。 */ autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { // 拋出 NoUniqueBeanDefinitionException 異常 return descriptor.resolveNotUnique(type, matchingBeans); } else { return null; } } // 根據(jù)解析出的 autowiredBeanName,獲取相應(yīng)的候選項(xiàng) instanceCandidate = matchingBeans.get(autowiredBeanName); } else { // 只有一個(gè)候選項(xiàng),直接取出來即可 Map.Entry entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } if (autowiredBeanNames != null) { autowiredBeanNames.add(autowiredBeanName); } // 返回候選項(xiàng)實(shí)例,如果實(shí)例是 Class 類型,則調(diào)用 beanFactory.getBean(String, Class) 獲取相應(yīng)的 bean。否則直接返回即可 return (instanceCandidate instanceof Class ? descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate); } finally { ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); } }
由上面的代碼可以看出,doResolveDependency 這個(gè)方法還是挺復(fù)雜的。這里我就不繼續(xù)分析 doResolveDependency 所調(diào)用的方法了,對(duì)于這些方法,我也是似懂非懂。好了,本節(jié)的最后我們來總結(jié)一下 doResolveDependency 的執(zhí)行流程吧,如下:
首先將 beanName 和 requiredType 作為參數(shù),并嘗試從 BeanFactory 中獲取與此對(duì)于的 bean。若獲取成功,就可以提前結(jié)束 doResolveDependency 的邏輯。
處理 @value 注解
解析數(shù)組、List、Map 等類型的依賴,如果解析結(jié)果不為空,則返回結(jié)果
根據(jù)類型查找合適的候選項(xiàng)
如果候選項(xiàng)的數(shù)量為0,則拋出異常。為1,直接從候選列表中取出即可。若候選項(xiàng)數(shù)量 > 1,則在多個(gè)候選項(xiàng)中確定最優(yōu)候選項(xiàng),若無法確定則拋出異常
若候選項(xiàng)是 Class 類型,表明候選項(xiàng)還沒實(shí)例化,此時(shí)通過 BeanFactory.getBean 方法對(duì)其進(jìn)行實(shí)例化。若候選項(xiàng)是非 Class 類型,則表明已經(jīng)完成了實(shí)例化,此時(shí)直接返回即可。
好了,本節(jié)的內(nèi)容先到這里。如果有分析錯(cuò)的地方,歡迎大家指出來。
2.4 applyPropertyValues 方法分析經(jīng)過了上面的流程,現(xiàn)在終于可以將屬性值注入到 bean 對(duì)象中了。當(dāng)然,這里還不能立即將屬性值注入到對(duì)象中,因?yàn)樵?Spring 配置文件中屬性值都是以 String 類型進(jìn)行配置的,所以 Spring 框架需要對(duì) String 類型進(jìn)行轉(zhuǎn)換。除此之外,對(duì)于 ref 屬性,這里還需要根據(jù) ref 屬性值解析依賴。還有一些其他操作,這里就不多說了,更多的信息我們一起在源碼探尋。
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; Listoriginal; if (pvs instanceof MutablePropertyValues) { mpvs = (MutablePropertyValues) pvs; // 如果屬性列表 pvs 被轉(zhuǎn)換過,則直接返回即可 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) { // 如果屬性值被轉(zhuǎn)換過,則就不需要再次轉(zhuǎn)換 if (pv.isConverted()) { deepCopy.add(pv); } else { String propertyName = pv.getName(); Object originalValue = pv.getValue(); /* * 解析屬性值。舉例說明,先看下面的配置: * * * * * 上面是一款電腦的配置信息,每個(gè) property 配置經(jīng)過下面的方法解析后,返回如下結(jié)果: * propertyName = "manufacturer", resolvedValue = "Apple" * propertyName = "width", resolvedValue = "280" * propertyName = "cpu", resolvedValue = "CPU@1234" 注:resolvedValue 是一個(gè)對(duì)象 * propertyName = "interface", resolvedValue = ["USB", "HDMI", "Thunderbolt"] * * 如上所示,resolveValueIfNecessary 會(huì)將 ref 解析為具體的對(duì)象,將* * * * **
*USB *HDMI *Thunderbolt ** 標(biāo)簽轉(zhuǎn)換為 List 對(duì)象等。對(duì)于 int 類型的配置,這里并未做轉(zhuǎn)換,所以 * width = "280",還是字符串。除了解析上面幾種類型,該方法還會(huì)解析
、 * 、 等集合配置 */ Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); Object convertedValue = resolvedValue; /* * convertible 表示屬性值是否可轉(zhuǎn)換,由兩個(gè)條件合成而來。第一個(gè)條件不難理解,解釋 * 一下第二個(gè)條件。第二個(gè)條件用于檢測 propertyName 是否是 nested 或者 indexed, * 直接舉例說明吧: * * public class Room { * private Door door = new Door(); * } * * room 對(duì)象里面包含了 door 對(duì)象,如果我們想向 door 對(duì)象中注入屬性值,則可以這樣配置: * * * * * isNestedOrIndexedProperty 會(huì)根據(jù) propertyName 中是否包含 . 或 [ 返回 * true 和 false。包含則返回 true,否則返回 false。關(guān)于 nested 類型的屬性,我 * 沒在實(shí)踐中用過,所以不知道上面舉的例子是不是合理。若不合理,歡迎指正,也請(qǐng)多多指教。 * 關(guān)于 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); // 對(duì)于一般的屬性,convertible 通常為 true if (convertible) { // 對(duì)屬性值的類型進(jìn)行轉(zhuǎn)換,比如將 String 類型的屬性值 "123" 轉(zhuǎn)為 Integer 類型的 123 convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter); } /* * 如果 originalValue 是通過 autowireByType 或 autowireByName 解析而來, * 那么此處條件成立,即 (resolvedValue == originalValue) = true */ if (resolvedValue == originalValue) { if (convertible) { // 將 convertedValue 設(shè)置到 pv 中,后續(xù)再次創(chuàng)建同一個(gè) bean 時(shí),就無需再次進(jìn)行轉(zhuǎn)換了 pv.setConvertedValue(convertedValue); } deepCopy.add(pv); } /* * 如果原始值 originalValue 是 TypedStringValue,且轉(zhuǎn)換后的值 * convertedValue 不是 Collection 或數(shù)組類型,則將轉(zhuǎn)換后的值存入到 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 { // 將所有的屬性值設(shè)置到 bean 實(shí)例中 bw.setPropertyValues(new MutablePropertyValues(deepCopy)); } catch (BeansException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Error setting property values", ex); } }*
以上就是 applyPropertyValues 方法的源碼,配合著我寫的注釋,應(yīng)該可以理解這個(gè)方法的流程。這個(gè)方法也調(diào)用了很多其他的方法,如果大家跟下去的話,會(huì)發(fā)現(xiàn)這些方法的調(diào)用棧也是很深的,比較復(fù)雜。這里說一下 bw.setPropertyValues 這個(gè)方法,如果大家跟到這個(gè)方法的調(diào)用棧的最底部,會(huì)發(fā)現(xiàn)這個(gè)方法是通過調(diào)用對(duì)象的 setter 方法進(jìn)行屬性設(shè)置的。這里貼一下簡化后的代碼:
public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper { // 省略部分代碼 private class BeanPropertyHandler extends PropertyHandler { @Override public void setValue(final Object object, Object valueToApply) throws Exception { // 獲取 writeMethod,也就是 setter 方法 final Method writeMethod = this.pd.getWriteMethod(); if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) { writeMethod.setAccessible(true); } final Object value = valueToApply; // 調(diào)用 setter 方法,getWrappedInstance() 返回的是 bean 對(duì)象 writeMethod.invoke(getWrappedInstance(), value); } } }
好了,本節(jié)的最后來總結(jié)一下 applyPropertyValues 方法的執(zhí)行流程吧,如下:
檢測屬性值列表是否已轉(zhuǎn)換過的,若轉(zhuǎn)換過,則直接填充屬性,無需再次轉(zhuǎn)換
遍歷屬性值列表 pvs,解析原始值 originalValue,得到解析值 resolvedValue
對(duì)解析后的屬性值 resolvedValue 進(jìn)行類型轉(zhuǎn)換
將類型轉(zhuǎn)換后的屬性值設(shè)置到 PropertyValue 對(duì)象中,并將 PropertyValue 對(duì)象存入 deepCopy 集合中
將 deepCopy 中的屬性信息注入到 bean 對(duì)象中
3. 總結(jié)本文對(duì) populateBean 方法及其所調(diào)用的 autowireByName、autowireByType 和 applyPropertyValues 做了較為詳細(xì)的分析,不知道大家看完后感覺如何。我說一下我的感受吧,從我看 Spring IOC 部分的源碼到現(xiàn)在寫了5篇關(guān)于 IOC 部分的源碼分析文章,總體感覺 Spring 的源碼還是很復(fù)雜的,調(diào)用層次很深。如果想對(duì)源碼有一個(gè)比較好的理解,需要不少的時(shí)間去分析,調(diào)試源碼??偟膩碚f,不容易。當(dāng)然,我的水平有限。如果大家自己去閱讀源碼,可能會(huì)覺得也沒這么難啊。
好了,其他的就不多說了。如果本文中有分析錯(cuò)的地方,歡迎大家指正。最后感謝大家的閱讀。
本文在知識(shí)共享許可協(xié)議 4.0 下發(fā)布,轉(zhuǎn)載需在明顯位置處注明出處附錄:Spring 源碼分析文章列表 Ⅰ. IOC
作者:coolblog.xyz
本文同步發(fā)布在我的個(gè)人博客:http://www.coolblog.xyz
更新時(shí)間 | 標(biāo)題 |
---|---|
2018-05-30 | Spring IOC 容器源碼分析系列文章導(dǎo)讀 |
2018-06-01 | Spring IOC 容器源碼分析 - 獲取單例 bean |
2018-06-04 | Spring IOC 容器源碼分析 - 創(chuàng)建單例 bean 的過程 |
2018-06-06 | Spring IOC 容器源碼分析 - 創(chuàng)建原始 bean 對(duì)象 |
2018-06-08 | Spring IOC 容器源碼分析 - 循環(huán)依賴的解決辦法 |
2018-06-11 | Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對(duì)象 |
2018-06-11 | Spring IOC 容器源碼分析 - 余下的初始化工作 |
更新時(shí)間 | 標(biāo)題 |
---|---|
2018-06-17 | Spring AOP 源碼分析系列文章導(dǎo)讀 |
2018-06-20 | Spring AOP 源碼分析 - 篩選合適的通知器 |
2018-06-20 | Spring AOP 源碼分析 - 創(chuàng)建代理對(duì)象 |
2018-06-22 | Spring AOP 源碼分析 - 攔截器鏈的執(zhí)行過程 |
更新時(shí)間 | 標(biāo)題 |
---|---|
2018-06-29 | Spring MVC 原理探秘 - 一個(gè)請(qǐng)求的旅行過程 |
2018-06-30 | Spring MVC 原理探秘 - 容器的創(chuàng)建過程 |
本作品采用知識(shí)共享署名-非商業(yè)性使用-禁止演繹 4.0 國際許可協(xié)議進(jìn)行許可。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/69718.html