摘要:源碼分析源碼一覽本節(jié),我們先來看一下填充屬性的方法,即。所有的屬性值是在方法中統(tǒng)一被注入到對象中的。檢測是否存在與相關(guān)的或。這樣可以在很大程度上降低源碼分析的難度。若候選項(xiàng)是非類型,則表明已經(jīng)完成了實(shí)例化,此時直接返回即可。
1. 簡介
本篇文章,我們來一起了解一下 Spring 是如何將配置文件中的屬性值填充到 bean 對象中的。我在前面幾篇文章中介紹過 Spring 創(chuàng)建 bean 的流程,即 Spring 先通過反射創(chuàng)建一個原始的 bean 對象,然后再向這個原始的 bean 對象中填充屬性。對于填充屬性這個過程,簡單點(diǎn)來說,JavaBean 的每個屬性通常都有 getter/setter 方法,我們可以直接調(diào)用 setter 方法將屬性值設(shè)置進(jìn)去。當(dāng)然,這樣做還是太簡單了,填充屬性的過程中還有許多事情要做。比如在 Spring 配置中,所有屬性值都是以字符串的形式進(jìn)行配置的,我們在將這些屬性值賦值給對象的成員變量時,要根據(jù)變量類型進(jìn)行相應(yīng)的類型轉(zhuǎn)換。對于一些集合類的配置,比如 、
關(guān)于屬性填充的一些知識,本章先介紹這里。接下來,我們深入到源碼中,從源碼中了解屬性填充的整個過程。
2. 源碼分析 2.1 populateBean 源碼一覽本節(jié),我們先來看一下填充屬性的方法,即 populateBean。該方法并不復(fù)雜,但它所調(diào)用的一些方法比較復(fù)雜。不過好在我們這里只需要知道這些方法都有什么用就行了,暫時不用糾結(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 類型的后置處理器一個修改 * bean 狀態(tài)的機(jī)會。關(guān)于這段后置引用,官方的解釋是:讓用戶可以自定義屬性注入。比如用戶實(shí)現(xiàn)一 * 個 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 幫忙填充了。此時直接返回即可 */ 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 對象前,對屬性的值進(jìn)行相應(yīng)的處理, * 比如可以修改某些屬性的值。這時注入到 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; // 對屬性進(jìn)行后置處理 pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } } } if (needsDepCheck) { checkDependencies(beanName, mbd, filteredPds, pvs); } } // 應(yīng)用屬性值到 bean 對象中 applyPropertyValues(beanName, mbd, bw, pvs); }
上面的源碼注釋的比較詳細(xì)了,下面我們來總結(jié)一下這個方法的執(zhí)行流程。如下:
獲取屬性列表 pvs
在屬性被填充到 bean 前,應(yīng)用后置處理自定義屬性填充
根據(jù)名稱或類型解析相關(guān)依賴
再次應(yīng)用后置處理,用于動態(tài)修改屬性列表 pvs 的內(nèi)容
將屬性應(yīng)用到 bean 對象中
注意第3步,也就是根據(jù)名稱或類型解析相關(guān)依賴(autowire)。該邏輯只會解析依賴,并不會將解析出的依賴立即注入到 bean 對象中。所有的屬性值是在 applyPropertyValues 方法中統(tǒng)一被注入到 bean 對象中的。
在下面的章節(jié)中,我將會對 populateBean 方法中比較重要的幾個方法調(diào)用進(jìn)行分析,也就是第3步和第5步中的三個方法。好了,本節(jié)先到這里。
2.2 autowireByName 方法分析本節(jié)來分析一下 autowireByName 方法的代碼,其實(shí)這個方法根據(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 添加到屬性列表中即可。既然這個方法比較簡單,那我也就不多說了,繼續(xù)下面的分析。
2.3 autowireByType 方法分析本節(jié)我們來分析一下 autowireByName 的孿生兄弟 autowireByType,相較于 autowireByName,autowireByType 則要復(fù)雜一些,復(fù)雜之處在于解析依賴的過程。不過也沒關(guān)系,如果我們不過于糾結(jié)細(xì)節(jié),我們完全可以把一些復(fù)雜的地方當(dāng)做一個黑盒,我們只需要要知道這個黑盒有什么用即可。這樣可以在很大程度上降低源碼分析的難度。好了,其他的就不多說了,咱們來分析源碼吧。
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)建依賴描述對象 DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); /* * 下面的方法用于解析依賴。過程比較復(fù)雜,先把這里看成一個黑盒,我們只要知道這 * 個方法可以幫我們解析出合適的依賴即可。 */ 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ù)對象 MethodParameter,隨后再根據(jù) MethodParameter 對象獲取依賴描述符對象,整個過程為 beanName → PropertyDescriptor → MethodParameter → DependencyDescriptor。在獲取到依賴描述符對象后,再根據(jù)依賴描述符解析出合適的依賴。最后將解析出的結(jié)果存入屬性列表 pvs 中即可。
關(guān)于 autowireByType 方法中出現(xiàn)的幾種描述符對象,大家自己去看一下他們的實(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; } /* * 按類型查找候選列表,如果某個類型已經(jīng)被實(shí)例化,則返回相應(yīng)的實(shí)例。 * 比如下面的配置: * * * * * * MongoDao 和 MySqlDao 均實(shí)現(xiàn)自 Dao 接口,Service 對象(不是接口)中有一個 Dao * 類型的屬性?,F(xiàn)在根據(jù)類型自動注入 Dao 的實(shí)現(xiàn)類。這里有兩個候選 bean,一個是 * mongoDao,另一個是 mysqlDao,其中 mongoDao 在 service 之前實(shí)例化, * mysqlDao 在 service 之后實(shí)例化。此時 findAutowireCandidates 方法會返回如下的結(jié)果: * * matchingBeans = [ , ] * * 注意 mysqlDao 還未實(shí)例化,所以返回的是 MySqlDao.class。 * * findAutowireCandidates 這個方法邏輯比較復(fù)雜,我簡單說一下它的工作流程吧,如下: * 1. 從 BeanFactory 中獲取某種類型 bean 的名稱,比如上面的配置中 * mongoDao 和 mysqlDao 均實(shí)現(xiàn)了 Dao 接口,所以他們是同一種類型的 bean。 * 2. 遍歷上一步得到的名稱列表,并判斷 bean 名稱對應(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,則表明存在多個可注入的候選項(xiàng),這里判斷使用哪一個 * 候選項(xiàng)。比如下面的配置: * * * * * mongoDao 的配置中存在 primary 屬性,所以 mongoDao 會被選為最終的候選項(xiàng)。如 * 果兩個 bean 配置都沒有 primary 屬性,則需要根據(jù)優(yōu)先級選擇候選項(xiàng)。優(yōu)先級這一塊 * 的邏輯沒細(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 { // 只有一個候選項(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 這個方法還是挺復(fù)雜的。這里我就不繼續(xù)分析 doResolveDependency 所調(diào)用的方法了,對于這些方法,我也是似懂非懂。好了,本節(jié)的最后我們來總結(jié)一下 doResolveDependency 的執(zhí)行流程吧,如下:
首先將 beanName 和 requiredType 作為參數(shù),并嘗試從 BeanFactory 中獲取與此對于的 bean。若獲取成功,就可以提前結(jié)束 doResolveDependency 的邏輯。
處理 @value 注解
解析數(shù)組、List、Map 等類型的依賴,如果解析結(jié)果不為空,則返回結(jié)果
根據(jù)類型查找合適的候選項(xiàng)
如果候選項(xiàng)的數(shù)量為0,則拋出異常。為1,直接從候選列表中取出即可。若候選項(xiàng)數(shù)量 > 1,則在多個候選項(xiàng)中確定最優(yōu)候選項(xiàng),若無法確定則拋出異常
若候選項(xiàng)是 Class 類型,表明候選項(xiàng)還沒實(shí)例化,此時通過 BeanFactory.getBean 方法對其進(jìn)行實(shí)例化。若候選項(xiàng)是非 Class 類型,則表明已經(jīng)完成了實(shí)例化,此時直接返回即可。
好了,本節(jié)的內(nèi)容先到這里。如果有分析錯的地方,歡迎大家指出來。
2.4 applyPropertyValues 方法分析經(jīng)過了上面的流程,現(xiàn)在終于可以將屬性值注入到 bean 對象中了。當(dāng)然,這里還不能立即將屬性值注入到對象中,因?yàn)樵?Spring 配置文件中屬性值都是以 String 類型進(jìn)行配置的,所以 Spring 框架需要對 String 類型進(jìn)行轉(zhuǎn)換。除此之外,對于 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(); /* * 解析屬性值。舉例說明,先看下面的配置: * * * * * 上面是一款電腦的配置信息,每個 property 配置經(jīng)過下面的方法解析后,返回如下結(jié)果: * propertyName = "manufacturer", resolvedValue = "Apple" * propertyName = "width", resolvedValue = "280" * propertyName = "cpu", resolvedValue = "CPU@1234" 注:resolvedValue 是一個對象 * propertyName = "interface", resolvedValue = ["USB", "HDMI", "Thunderbolt"] * * 如上所示,resolveValueIfNecessary 會將 ref 解析為具體的對象,將* * * * **
*USB *HDMI *Thunderbolt ** 標(biāo)簽轉(zhuǎn)換為 List 對象等。對于 int 類型的配置,這里并未做轉(zhuǎn)換,所以 * width = "280",還是字符串。除了解析上面幾種類型,該方法還會解析
、 * 、 等集合配置 */ Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); Object convertedValue = resolvedValue; /* * convertible 表示屬性值是否可轉(zhuǎn)換,由兩個條件合成而來。第一個條件不難理解,解釋 * 一下第二個條件。第二個條件用于檢測 propertyName 是否是 nested 或者 indexed, * 直接舉例說明吧: * * public class Room { * private Door door = new Door(); * } * * room 對象里面包含了 door 對象,如果我們想向 door 對象中注入屬性值,則可以這樣配置: * * * * * isNestedOrIndexedProperty 會根據(jù) propertyName 中是否包含 . 或 [ 返回 * true 和 false。包含則返回 true,否則返回 false。關(guān)于 nested 類型的屬性,我 * 沒在實(shí)踐中用過,所以不知道上面舉的例子是不是合理。若不合理,歡迎指正,也請多多指教。 * 關(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); // 對于一般的屬性,convertible 通常為 true if (convertible) { // 對屬性值的類型進(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)建同一個 bean 時,就無需再次進(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)該可以理解這個方法的流程。這個方法也調(diào)用了很多其他的方法,如果大家跟下去的話,會發(fā)現(xiàn)這些方法的調(diào)用棧也是很深的,比較復(fù)雜。這里說一下 bw.setPropertyValues 這個方法,如果大家跟到這個方法的調(diào)用棧的最底部,會發(fā)現(xiàn)這個方法是通過調(diào)用對象的 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 對象 writeMethod.invoke(getWrappedInstance(), value); } } }
好了,本節(jié)的最后來總結(jié)一下 applyPropertyValues 方法的執(zhí)行流程吧,如下:
檢測屬性值列表是否已轉(zhuǎn)換過的,若轉(zhuǎn)換過,則直接填充屬性,無需再次轉(zhuǎn)換
遍歷屬性值列表 pvs,解析原始值 originalValue,得到解析值 resolvedValue
對解析后的屬性值 resolvedValue 進(jìn)行類型轉(zhuǎn)換
將類型轉(zhuǎn)換后的屬性值設(shè)置到 PropertyValue 對象中,并將 PropertyValue 對象存入 deepCopy 集合中
將 deepCopy 中的屬性信息注入到 bean 對象中
3. 總結(jié)本文對 populateBean 方法及其所調(diào)用的 autowireByName、autowireByType 和 applyPropertyValues 做了較為詳細(xì)的分析,不知道大家看完后感覺如何。我說一下我的感受吧,從我看 Spring IOC 部分的源碼到現(xiàn)在寫了5篇關(guān)于 IOC 部分的源碼分析文章,總體感覺 Spring 的源碼還是很復(fù)雜的,調(diào)用層次很深。如果想對源碼有一個比較好的理解,需要不少的時間去分析,調(diào)試源碼??偟膩碚f,不容易。當(dāng)然,我的水平有限。如果大家自己去閱讀源碼,可能會覺得也沒這么難啊。
好了,其他的就不多說了。如果本文中有分析錯的地方,歡迎大家指正。最后感謝大家的閱讀。
本文在知識共享許可協(xié)議 4.0 下發(fā)布,轉(zhuǎn)載需在明顯位置處注明出處附錄:Spring 源碼分析文章列表 Ⅰ. IOC
作者:coolblog.xyz
本文同步發(fā)布在我的個人博客:http://www.coolblog.xyz
更新時間 | 標(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 對象 |
2018-06-08 | Spring IOC 容器源碼分析 - 循環(huán)依賴的解決辦法 |
2018-06-11 | Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對象 |
2018-06-11 | Spring IOC 容器源碼分析 - 余下的初始化工作 |
更新時間 | 標(biāo)題 |
---|---|
2018-06-17 | Spring AOP 源碼分析系列文章導(dǎo)讀 |
2018-06-20 | Spring AOP 源碼分析 - 篩選合適的通知器 |
2018-06-20 | Spring AOP 源碼分析 - 創(chuàng)建代理對象 |
2018-06-22 | Spring AOP 源碼分析 - 攔截器鏈的執(zhí)行過程 |
更新時間 | 標(biāo)題 |
---|---|
2018-06-29 | Spring MVC 原理探秘 - 一個請求的旅行過程 |
2018-06-30 | Spring MVC 原理探秘 - 容器的創(chuàng)建過程 |
本作品采用知識共享署名-非商業(yè)性使用-禁止演繹 4.0 國際許可協(xié)議進(jìn)行許可。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69718.html
摘要:實(shí)例化時,發(fā)現(xiàn)又依賴于。一些緩存的介紹在進(jìn)行源碼分析前,我們先來看一組緩存的定義。可是看完源碼后,我們似乎仍然不知道這些源碼是如何解決循環(huán)依賴問題的。 1. 簡介 本文,我們來看一下 Spring 是如何解決循環(huán)依賴問題的。在本篇文章中,我會首先向大家介紹一下什么是循環(huán)依賴。然后,進(jìn)入源碼分析階段。為了更好的說明 Spring 解決循環(huán)依賴的辦法,我將會從獲取 bean 的方法getB...
摘要:簡介本篇文章是容器源碼分析系列文章的最后一篇文章,本篇文章所分析的對象是方法,該方法用于對已完成屬性填充的做最后的初始化工作。后置處理器是拓展點(diǎn)之一,通過實(shí)現(xiàn)后置處理器接口,我們就可以插手的初始化過程。 1. 簡介 本篇文章是Spring IOC 容器源碼分析系列文章的最后一篇文章,本篇文章所分析的對象是 initializeBean 方法,該方法用于對已完成屬性填充的 bean 做最...
摘要:關(guān)于創(chuàng)建實(shí)例的過程,我將會分幾篇文章進(jìn)行分析。源碼分析創(chuàng)建實(shí)例的入口在正式分析方法前,我們先來看看方法是在哪里被調(diào)用的。時,表明方法不存在,此時拋出異常。該變量用于表示是否提前暴露單例,用于解決循環(huán)依賴。 1. 簡介 在上一篇文章中,我比較詳細(xì)的分析了獲取 bean 的方法,也就是getBean(String)的實(shí)現(xiàn)邏輯。對于已實(shí)例化好的單例 bean,getBean(String) ...
摘要:本文是容器源碼分析系列文章的第一篇文章,將會著重介紹的一些使用方法和特性,為后續(xù)的源碼分析文章做鋪墊。我們可以通過這兩個別名獲取到這個實(shí)例,比如下面的測試代碼測試結(jié)果如下本小節(jié),我們來了解一下這個特性。 1. 簡介 Spring 是一個輕量級的企業(yè)級應(yīng)用開發(fā)框架,于 2004 年由 Rod Johnson 發(fā)布了 1.0 版本。經(jīng)過十幾年的迭代,現(xiàn)在的 Spring 框架已經(jīng)非常成熟了...
摘要:你也會了解到構(gòu)造對象的兩種策略。構(gòu)造方法參數(shù)數(shù)量低于配置的參數(shù)數(shù)量,則忽略當(dāng)前構(gòu)造方法,并重試。通過默認(rèn)構(gòu)造方法創(chuàng)建對象看完了上面冗長的邏輯,本節(jié)來看點(diǎn)輕松的吧通過默認(rèn)構(gòu)造方法創(chuàng)建對象。 1. 簡介 本篇文章是上一篇文章(創(chuàng)建單例 bean 的過程)的延續(xù)。在上一篇文章中,我們從戰(zhàn)略層面上領(lǐng)略了doCreateBean方法的全過程。本篇文章,我們就從戰(zhàn)術(shù)的層面上,詳細(xì)分析doCreat...
閱讀 3483·2019-08-30 13:15
閱讀 1423·2019-08-29 18:34
閱讀 853·2019-08-29 15:18
閱讀 3505·2019-08-29 11:21
閱讀 3281·2019-08-29 10:55
閱讀 3730·2019-08-26 10:36
閱讀 1896·2019-08-23 18:37
閱讀 1854·2019-08-23 16:57