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