资讯专栏INFORMATION COLUMN

Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

SKYZACK / 1528人阅读

摘要:源码分析源码一览本节,我们先来看一下填充属性的方法,即。所有的属性值是在方法中统一被注入到对象中的。检测是否存在与相关的或。这样可以在很大程度上降低源码分析的难度。若候选项是非类型,则表明已经完成了实例化,此时直接返回即可。

1. 简介

本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的。我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反射创建一个原始的 bean 对象,然后再向这个原始的 bean 对象中填充属性。对于填充属性这个过程,简单点来说,JavaBean 的每个属性通常都有 getter/setter 方法,我们可以直接调用 setter 方法将属性值设置进去。当然,这样做还是太简单了,填充属性的过程中还有许多事情要做。比如在 Spring 配置中,所有属性值都是以字符串的形式进行配置的,我们在将这些属性值赋值给对象的成员变量时,要根据变量类型进行相应的类型转换。对于一些集合类的配置,比如 ,还要将这些配置转换成相应的集合对象才能进行后续的操作。除此之外,如果用户配置了自动注入(autowire = byName/byType),Spring 还要去为自动注入的属性寻找合适的注入项。由此可以见,属性填充的整个过程还是很复杂的,并非是简单调用 setter 方法设置属性值即可。

关于属性填充的一些知识,本章先介绍这里。接下来,我们深入到源码中,从源码中了解属性填充的整个过程。

2. 源码分析 2.1 populateBean 源码一览

本节,我们先来看一下填充属性的方法,即 populateBean。该方法并不复杂,但它所调用的一些方法比较复杂。不过好在我们这里只需要知道这些方法都有什么用就行了,暂时不用纠结细节。好了,下面看源码吧。

</>复制代码

  1. protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
  2. // 获取属性列表
  3. PropertyValues pvs = mbd.getPropertyValues();
  4. if (bw == null) {
  5. if (!pvs.isEmpty()) {
  6. throw new BeanCreationException(
  7. mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
  8. }
  9. else {
  10. return;
  11. }
  12. }
  13. boolean continueWithPropertyPopulation = true;
  14. /*
  15. * 在属性被填充前,给 InstantiationAwareBeanPostProcessor 类型的后置处理器一个修改
  16. * bean 状态的机会。关于这段后置引用,官方的解释是:让用户可以自定义属性注入。比如用户实现一
  17. * 个 InstantiationAwareBeanPostProcessor 类型的后置处理器,并通过
  18. * postProcessAfterInstantiation 方法向 bean 的成员变量注入自定义的信息。当然,如果无
  19. * 特殊需求,直接使用配置中的信息注入即可。另外,Spring 并不建议大家直接实现
  20. * InstantiationAwareBeanPostProcessor 接口,如果想实现这种类型的后置处理器,更建议
  21. * 通过继承 InstantiationAwareBeanPostProcessorAdapter 抽象类实现自定义后置处理器。
  22. */
  23. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  24. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  25. if (bp instanceof InstantiationAwareBeanPostProcessor) {
  26. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
  27. if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
  28. continueWithPropertyPopulation = false;
  29. break;
  30. }
  31. }
  32. }
  33. }
  34. /*
  35. * 如果上面设置 continueWithPropertyPopulation = false,表明用户可能已经自己填充了
  36. * bean 的属性,不需要 Spring 帮忙填充了。此时直接返回即可
  37. */
  38. if (!continueWithPropertyPopulation) {
  39. return;
  40. }
  41. // 根据名称或类型注入依赖
  42. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
  43. mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
  44. MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
  45. // 通过属性名称注入依赖
  46. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
  47. autowireByName(beanName, mbd, bw, newPvs);
  48. }
  49. // 通过属性类型注入依赖
  50. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
  51. autowireByType(beanName, mbd, bw, newPvs);
  52. }
  53. pvs = newPvs;
  54. }
  55. boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
  56. boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
  57. /*
  58. * 这里又是一种后置处理,用于在 Spring 填充属性到 bean 对象前,对属性的值进行相应的处理,
  59. * 比如可以修改某些属性的值。这时注入到 bean 中的值就不是配置文件中的内容了,
  60. * 而是经过后置处理器修改后的内容
  61. */
  62. if (hasInstAwareBpps || needsDepCheck) {
  63. PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
  64. if (hasInstAwareBpps) {
  65. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  66. if (bp instanceof InstantiationAwareBeanPostProcessor) {
  67. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
  68. // 对属性进行后置处理
  69. pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
  70. if (pvs == null) {
  71. return;
  72. }
  73. }
  74. }
  75. }
  76. if (needsDepCheck) {
  77. checkDependencies(beanName, mbd, filteredPds, pvs);
  78. }
  79. }
  80. // 应用属性值到 bean 对象中
  81. applyPropertyValues(beanName, mbd, bw, pvs);
  82. }

上面的源码注释的比较详细了,下面我们来总结一下这个方法的执行流程。如下:

获取属性列表 pvs

在属性被填充到 bean 前,应用后置处理自定义属性填充

根据名称或类型解析相关依赖

再次应用后置处理,用于动态修改属性列表 pvs 的内容

将属性应用到 bean 对象中

注意第3步,也就是根据名称或类型解析相关依赖(autowire)。该逻辑只会解析依赖,并不会将解析出的依赖立即注入到 bean 对象中。所有的属性值是在 applyPropertyValues 方法中统一被注入到 bean 对象中的。

在下面的章节中,我将会对 populateBean 方法中比较重要的几个方法调用进行分析,也就是第3步和第5步中的三个方法。好了,本节先到这里。

2.2 autowireByName 方法分析

本节来分析一下 autowireByName 方法的代码,其实这个方法根据方法名,大家应该知道它有什么用了。所以我也就不啰嗦了,咱们直奔主题,直接分析源码:

</>复制代码

  1. protected void autowireByName(
  2. String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
  3. /*
  4. * 获取非简单类型属性的名称,且该属性未被配置在配置文件中。这里从反面解释一下什么是"非简单类型"
  5. * 属性,我们先来看看 Spring 认为的"简单类型"属性有哪些,如下:
  6. * 1. CharSequence 接口的实现类,比如 String
  7. * 2. Enum
  8. * 3. Date
  9. * 4. URI/URL
  10. * 5. Number 的继承类,比如 Integer/Long
  11. * 6. byte/short/int... 等基本类型
  12. * 7. Locale
  13. * 8. 以上所有类型的数组形式,比如 String[]Date[]int[] 等等
  14. *
  15. * 除了要求非简单类型的属性外,还要求属性未在配置文件中配置过,也就是 pvs.contains(pd.getName()) = false
  16. */
  17. String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
  18. for (String propertyName : propertyNames) {
  19. // 检测是否存在与 propertyName 相关的 bean 或 BeanDefinition。若存在,则调用 BeanFactory.getBean 方法获取 bean 实例
  20. if (containsBean(propertyName)) {
  21. // 从容器中获取相应的 bean 实例
  22. Object bean = getBean(propertyName);
  23. // 将解析出的 bean 存入到属性值列表(pvs)中
  24. pvs.add(propertyName, bean);
  25. registerDependentBean(propertyName, beanName);
  26. if (logger.isDebugEnabled()) {
  27. logger.debug("Added autowiring by name from bean name "" + beanName +
  28. "" via property "" + propertyName + "" to bean named "" + propertyName + """);
  29. }
  30. }
  31. else {
  32. if (logger.isTraceEnabled()) {
  33. logger.trace("Not autowiring property "" + propertyName + "" of bean "" + beanName +
  34. "" by name: no matching bean found");
  35. }
  36. }
  37. }
  38. }

autowireByName 方法的逻辑比较简单,该方法首先获取非简单类型属性的名称,然后再根据名称到容器中获取相应的 bean 实例,最后再将获取到的 bean 添加到属性列表中即可。既然这个方法比较简单,那我也就不多说了,继续下面的分析。

2.3 autowireByType 方法分析

本节我们来分析一下 autowireByName 的孪生兄弟 autowireByType,相较于 autowireByName,autowireByType 则要复杂一些,复杂之处在于解析依赖的过程。不过也没关系,如果我们不过于纠结细节,我们完全可以把一些复杂的地方当做一个黑盒,我们只需要要知道这个黑盒有什么用即可。这样可以在很大程度上降低源码分析的难度。好了,其他的就不多说了,咱们来分析源码吧。

</>复制代码

  1. protected void autowireByType(
  2. String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
  3. TypeConverter converter = getCustomTypeConverter();
  4. if (converter == null) {
  5. converter = bw;
  6. }
  7. Set autowiredBeanNames = new LinkedHashSet(4);
  8. // 获取非简单类型的属性
  9. String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
  10. for (String propertyName : propertyNames) {
  11. try {
  12. PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
  13. // 如果属性类型为 Object,则忽略,不做解析
  14. if (Object.class != pd.getPropertyType()) {
  15. /*
  16. * 获取 setter 方法(write method)的参数信息,比如参数在参数列表中的
  17. * 位置,参数类型,以及该参数所归属的方法等信息
  18. */
  19. MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
  20. // Do not allow eager init for type matching in case of a prioritized post-processor.
  21. boolean eager = !PriorityOrdered.class.isAssignableFrom(bw.getWrappedClass());
  22. // 创建依赖描述对象
  23. DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
  24. /*
  25. * 下面的方法用于解析依赖。过程比较复杂,先把这里看成一个黑盒,我们只要知道这
  26. * 个方法可以帮我们解析出合适的依赖即可。
  27. */
  28. Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
  29. if (autowiredArgument != null) {
  30. // 将解析出的 bean 存入到属性值列表(pvs)中
  31. pvs.add(propertyName, autowiredArgument);
  32. }
  33. for (String autowiredBeanName : autowiredBeanNames) {
  34. registerDependentBean(autowiredBeanName, beanName);
  35. if (logger.isDebugEnabled()) {
  36. logger.debug("Autowiring by type from bean name "" + beanName + "" via property "" +
  37. propertyName + "" to bean named "" + autowiredBeanName + """);
  38. }
  39. }
  40. autowiredBeanNames.clear();
  41. }
  42. }
  43. catch (BeansException ex) {
  44. throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
  45. }
  46. }
  47. }

如上所示,autowireByType 的代码本身并不复杂。和 autowireByName 一样,autowireByType 首先也是获取非简单类型属性的名称。然后再根据属性名获取属性描述符,并由属性描述符获取方法参数对象 MethodParameter,随后再根据 MethodParameter 对象获取依赖描述符对象,整个过程为 beanName → PropertyDescriptor → MethodParameter → DependencyDescriptor。在获取到依赖描述符对象后,再根据依赖描述符解析出合适的依赖。最后将解析出的结果存入属性列表 pvs 中即可。

关于 autowireByType 方法中出现的几种描述符对象,大家自己去看一下他们的实现吧,我就不分析了。接下来,我们来分析一下解析依赖的方法 resolveDependency。如下:

</>复制代码

  1. public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName,
  2. Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
  3. descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
  4. if (javaUtilOptionalClass == descriptor.getDependencyType()) {
  5. return new OptionalDependencyFactory().createOptionalDependency(descriptor, requestingBeanName);
  6. }
  7. else if (ObjectFactory.class == descriptor.getDependencyType() ||
  8. ObjectProvider.class == descriptor.getDependencyType()) {
  9. return new DependencyObjectProvider(descriptor, requestingBeanName);
  10. }
  11. else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
  12. return new Jsr330ProviderFactory().createDependencyProvider(descriptor, requestingBeanName);
  13. }
  14. else {
  15. Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
  16. descriptor, requestingBeanName);
  17. if (result == null) {
  18. // 解析依赖
  19. result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
  20. }
  21. return result;
  22. }
  23. }
  24. public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
  25. Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
  26. InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
  27. try {
  28. // 该方法最终调用了 beanFactory.getBean(String, Class),从容器中获取依赖
  29. Object shortcut = descriptor.resolveShortcut(this);
  30. // 如果容器中存在所需依赖,这里进行断路操作,提前结束依赖解析逻辑
  31. if (shortcut != null) {
  32. return shortcut;
  33. }
  34. Class type = descriptor.getDependencyType();
  35. // 处理 @value 注解
  36. Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
  37. if (value != null) {
  38. if (value instanceof String) {
  39. String strVal = resolveEmbeddedValue((String) value);
  40. BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
  41. value = evaluateBeanDefinitionString(strVal, bd);
  42. }
  43. TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
  44. return (descriptor.getField() != null ?
  45. converter.convertIfNecessary(value, type, descriptor.getField()) :
  46. converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
  47. }
  48. // 解析数组、list、map 等类型的依赖
  49. Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
  50. if (multipleBeans != null) {
  51. return multipleBeans;
  52. }
  53. /*
  54. * 按类型查找候选列表,如果某个类型已经被实例化,则返回相应的实例。
  55. * 比如下面的配置:
  56. *
  57. *
  58. *
  59. *
  60. *
  61. * MongoDao 和 MySqlDao 均实现自 Dao 接口,Service 对象(不是接口)中有一个 Dao
  62. * 类型的属性。现在根据类型自动注入 Dao 的实现类。这里有两个候选 bean,一个是
  63. * mongoDao,另一个是 mysqlDao,其中 mongoDao 在 service 之前实例化,
  64. * mysqlDao 在 service 之后实例化。此时 findAutowireCandidates 方法会返回如下的结果:
  65. *
  66. * matchingBeans = [ , ]
  67. *
  68. * 注意 mysqlDao 还未实例化,所以返回的是 MySqlDao.class
  69. *
  70. * findAutowireCandidates 这个方法逻辑比较复杂,我简单说一下它的工作流程吧,如下:
  71. * 1. 从 BeanFactory 中获取某种类型 bean 的名称,比如上面的配置中
  72. * mongoDao 和 mysqlDao 均实现了 Dao 接口,所以他们是同一种类型的 bean。
  73. * 2. 遍历上一步得到的名称列表,并判断 bean 名称对应的 bean 是否是合适的候选项,
  74. * 若合适则添加到候选列表中,并在最后返回候选列表
  75. *
  76. * findAutowireCandidates 比较复杂,我并未完全搞懂,就不深入分析了。见谅
  77. */
  78. Map matchingBeans = findAutowireCandidates(beanName, type, descriptor);
  79. if (matchingBeans.isEmpty()) {
  80. if (isRequired(descriptor)) {
  81. // 抛出 NoSuchBeanDefinitionException 异常
  82. raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
  83. }
  84. return null;
  85. }
  86. String autowiredBeanName;
  87. Object instanceCandidate;
  88. if (matchingBeans.size() > 1) {
  89. /*
  90. * matchingBeans.size() > 1,则表明存在多个可注入的候选项,这里判断使用哪一个
  91. * 候选项。比如下面的配置:
  92. *
  93. *
  94. *
  95. *
  96. * mongoDao 的配置中存在 primary 属性,所以 mongoDao 会被选为最终的候选项。如
  97. * 果两个 bean 配置都没有 primary 属性,则需要根据优先级选择候选项。优先级这一块
  98. * 的逻辑没细看,不多说了。
  99. */
  100. autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
  101. if (autowiredBeanName == null) {
  102. if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
  103. // 抛出 NoUniqueBeanDefinitionException 异常
  104. return descriptor.resolveNotUnique(type, matchingBeans);
  105. }
  106. else {
  107. return null;
  108. }
  109. }
  110. // 根据解析出的 autowiredBeanName,获取相应的候选项
  111. instanceCandidate = matchingBeans.get(autowiredBeanName);
  112. }
  113. else { // 只有一个候选项,直接取出来即可
  114. Map.Entry entry = matchingBeans.entrySet().iterator().next();
  115. autowiredBeanName = entry.getKey();
  116. instanceCandidate = entry.getValue();
  117. }
  118. if (autowiredBeanNames != null) {
  119. autowiredBeanNames.add(autowiredBeanName);
  120. }
  121. // 返回候选项实例,如果实例是 Class 类型,则调用 beanFactory.getBean(String, Class) 获取相应的 bean。否则直接返回即可
  122. return (instanceCandidate instanceof Class ?
  123. descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate);
  124. }
  125. finally {
  126. ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
  127. }
  128. }

由上面的代码可以看出,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 属性值解析依赖。还有一些其他操作,这里就不多说了,更多的信息我们一起在源码探寻。

</>复制代码

  1. protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
  2. if (pvs == null || pvs.isEmpty()) {
  3. return;
  4. }
  5. if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
  6. ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
  7. }
  8. MutablePropertyValues mpvs = null;
  9. List original;
  10. if (pvs instanceof MutablePropertyValues) {
  11. mpvs = (MutablePropertyValues) pvs;
  12. // 如果属性列表 pvs 被转换过,则直接返回即可
  13. if (mpvs.isConverted()) {
  14. try {
  15. bw.setPropertyValues(mpvs);
  16. return;
  17. }
  18. catch (BeansException ex) {
  19. throw new BeanCreationException(
  20. mbd.getResourceDescription(), beanName, "Error setting property values", ex);
  21. }
  22. }
  23. original = mpvs.getPropertyValueList();
  24. }
  25. else {
  26. original = Arrays.asList(pvs.getPropertyValues());
  27. }
  28. TypeConverter converter = getCustomTypeConverter();
  29. if (converter == null) {
  30. converter = bw;
  31. }
  32. BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
  33. List deepCopy = new ArrayList(original.size());
  34. boolean resolveNecessary = false;
  35. // 遍历属性列表
  36. for (PropertyValue pv : original) {
  37. // 如果属性值被转换过,则就不需要再次转换
  38. if (pv.isConverted()) {
  39. deepCopy.add(pv);
  40. }
  41. else {
  42. String propertyName = pv.getName();
  43. Object originalValue = pv.getValue();
  44. /*
  45. * 解析属性值。举例说明,先看下面的配置:
  46. *
  47. *
  48. *
  49. *
  50. *
  51. *
  52. *
  53. * USB
  54. * HDMI
  55. * Thunderbolt
  56. *
  57. *
  58. *
  59. *
  60. * 上面是一款电脑的配置信息,每个 property 配置经过下面的方法解析后,返回如下结果:
  61. * propertyName = "manufacturer", resolvedValue = "Apple"
  62. * propertyName = "width", resolvedValue = "280"
  63. * propertyName = "cpu", resolvedValue = "CPU@1234" 注:resolvedValue 是一个对象
  64. * propertyName = "interface", resolvedValue = ["USB", "HDMI", "Thunderbolt"]
  65. *
  66. * 如上所示,resolveValueIfNecessary 会将 ref 解析为具体的对象,将
  67. * 标签转换为 List 对象等。对于 int 类型的配置,这里并未做转换,所以
  68. * width = "280",还是字符串。除了解析上面几种类型,该方法还会解析
  69. * 等集合配置
  70. */
  71. Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
  72. Object convertedValue = resolvedValue;
  73. /*
  74. * convertible 表示属性值是否可转换,由两个条件合成而来。第一个条件不难理解,解释
  75. * 一下第二个条件。第二个条件用于检测 propertyName 是否是 nested 或者 indexed,
  76. * 直接举例说明吧:
  77. *
  78. * public class Room {
  79. * private Door door = new Door();
  80. * }
  81. *
  82. * room 对象里面包含了 door 对象,如果我们想向 door 对象中注入属性值,则可以这样配置:
  83. *
  84. *
  85. *
  86. *
  87. *
  88. * isNestedOrIndexedProperty 会根据 propertyName 中是否包含 . 或 [ 返回
  89. * truefalse。包含则返回 true,否则返回 false。关于 nested 类型的属性,我
  90. * 没在实践中用过,所以不知道上面举的例子是不是合理。若不合理,欢迎指正,也请多多指教。
  91. * 关于 nested 类型的属性,大家还可以参考 Spring 的官方文档:
  92. * https://docs.spring.io/spring/docs/4.3.17.RELEASE/spring-framework-reference/htmlsingle/#beans-beans-conventions
  93. */
  94. boolean convertible = bw.isWritableProperty(propertyName) &&
  95. !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
  96. // 对于一般的属性,convertible 通常为 true
  97. if (convertible) {
  98. // 对属性值的类型进行转换,比如将 String 类型的属性值 "123" 转为 Integer 类型的 123
  99. convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
  100. }
  101. /*
  102. * 如果 originalValue 是通过 autowireByType 或 autowireByName 解析而来,
  103. * 那么此处条件成立,即 (resolvedValue == originalValue) = true
  104. */
  105. if (resolvedValue == originalValue) {
  106. if (convertible) {
  107. // 将 convertedValue 设置到 pv 中,后续再次创建同一个 bean 时,就无需再次进行转换了
  108. pv.setConvertedValue(convertedValue);
  109. }
  110. deepCopy.add(pv);
  111. }
  112. /*
  113. * 如果原始值 originalValue 是 TypedStringValue,且转换后的值
  114. * convertedValue 不是 Collection 或数组类型,则将转换后的值存入到 pv 中。
  115. */
  116. else if (convertible && originalValue instanceof TypedStringValue &&
  117. !((TypedStringValue) originalValue).isDynamic() &&
  118. !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
  119. pv.setConvertedValue(convertedValue);
  120. deepCopy.add(pv);
  121. }
  122. else {
  123. resolveNecessary = true;
  124. deepCopy.add(new PropertyValue(pv, convertedValue));
  125. }
  126. }
  127. }
  128. if (mpvs != null && !resolveNecessary) {
  129. mpvs.setConverted();
  130. }
  131. try {
  132. // 将所有的属性值设置到 bean 实例中
  133. bw.setPropertyValues(new MutablePropertyValues(deepCopy));
  134. }
  135. catch (BeansException ex) {
  136. throw new BeanCreationException(
  137. mbd.getResourceDescription(), beanName, "Error setting property values", ex);
  138. }
  139. }

以上就是 applyPropertyValues 方法的源码,配合着我写的注释,应该可以理解这个方法的流程。这个方法也调用了很多其他的方法,如果大家跟下去的话,会发现这些方法的调用栈也是很深的,比较复杂。这里说一下 bw.setPropertyValues 这个方法,如果大家跟到这个方法的调用栈的最底部,会发现这个方法是通过调用对象的 setter 方法进行属性设置的。这里贴一下简化后的代码:

</>复制代码

  1. public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper {
  2. // 省略部分代码
  3. private class BeanPropertyHandler extends PropertyHandler {
  4. @Override
  5. public void setValue(final Object object, Object valueToApply) throws Exception {
  6. // 获取 writeMethod,也就是 setter 方法
  7. final Method writeMethod = this.pd.getWriteMethod();
  8. if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {
  9. writeMethod.setAccessible(true);
  10. }
  11. final Object value = valueToApply;
  12. // 调用 setter 方法,getWrappedInstance() 返回的是 bean 对象
  13. writeMethod.invoke(getWrappedInstance(), value);
  14. }
  15. }
  16. }

好了,本节的最后来总结一下 applyPropertyValues 方法的执行流程吧,如下:

检测属性值列表是否已转换过的,若转换过,则直接填充属性,无需再次转换

遍历属性值列表 pvs,解析原始值 originalValue,得到解析值 resolvedValue

对解析后的属性值 resolvedValue 进行类型转换

将类型转换后的属性值设置到 PropertyValue 对象中,并将 PropertyValue 对象存入 deepCopy 集合中

将 deepCopy 中的属性信息注入到 bean 对象中

3. 总结

本文对 populateBean 方法及其所调用的 autowireByName、autowireByType 和 applyPropertyValues 做了较为详细的分析,不知道大家看完后感觉如何。我说一下我的感受吧,从我看 Spring IOC 部分的源码到现在写了5篇关于 IOC 部分的源码分析文章,总体感觉 Spring 的源码还是很复杂的,调用层次很深。如果想对源码有一个比较好的理解,需要不少的时间去分析,调试源码。总的来说,不容易。当然,我的水平有限。如果大家自己去阅读源码,可能会觉得也没这么难啊。

好了,其他的就不多说了。如果本文中有分析错的地方,欢迎大家指正。最后感谢大家的阅读。

</>复制代码

  1. 本文在知识共享许可协议 4.0 下发布,转载需在明显位置处注明出处
    作者:coolblog.xyz
    本文同步发布在我的个人博客:http://www.coolblog.xyz
附录:Spring 源码分析文章列表 Ⅰ. IOC
更新时间 标题
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 容器源码分析 - 余下的初始化工作
Ⅱ. AOP
更新时间 标题
2018-06-17 Spring AOP 源码分析系列文章导读
2018-06-20 Spring AOP 源码分析 - 筛选合适的通知器
2018-06-20 Spring AOP 源码分析 - 创建代理对象
2018-06-22 Spring AOP 源码分析 - 拦截器链的执行过程
Ⅲ. MVC
更新时间 标题
2018-06-29 Spring MVC 原理探秘 - 一个请求的旅行过程
2018-06-30 Spring MVC 原理探秘 - 容器的创建过程


本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69718.html

相关文章

  • Spring IOC 容器源码分析 - 循环依赖的解决办法

    摘要:实例化时,发现又依赖于。一些缓存的介绍在进行源码分析前,我们先来看一组缓存的定义。可是看完源码后,我们似乎仍然不知道这些源码是如何解决循环依赖问题的。 1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的。在本篇文章中,我会首先向大家介绍一下什么是循环依赖。然后,进入源码分析阶段。为了更好的说明 Spring 解决循环依赖的办法,我将会从获取 bean 的方法getB...

    aikin 评论0 收藏0
  • Spring IOC 容器源码分析 - 余下的初始化工作

    摘要:简介本篇文章是容器源码分析系列文章的最后一篇文章,本篇文章所分析的对象是方法,该方法用于对已完成属性填充的做最后的初始化工作。后置处理器是拓展点之一,通过实现后置处理器接口,我们就可以插手的初始化过程。 1. 简介 本篇文章是Spring IOC 容器源码分析系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bean 做最...

    Alfred 评论0 收藏0
  • Spring IOC 容器源码分析 - 创建单例 bean 的过程

    摘要:关于创建实例的过程,我将会分几篇文章进行分析。源码分析创建实例的入口在正式分析方法前,我们先来看看方法是在哪里被调用的。时,表明方法不存在,此时抛出异常。该变量用于表示是否提前暴露单例,用于解决循环依赖。 1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑。对于已实例化好的单例 bean,getBean(String) ...

    mochixuan 评论0 收藏0
  • Spring IOC 容器源码分析系列文章导读

    摘要:本文是容器源码分析系列文章的第一篇文章,将会着重介绍的一些使用方法和特性,为后续的源码分析文章做铺垫。我们可以通过这两个别名获取到这个实例,比如下面的测试代码测试结果如下本小节,我们来了解一下这个特性。 1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本。经过十几年的迭代,现在的 Spring 框架已经非常成熟了...

    NSFish 评论0 收藏0
  • Spring IOC 容器源码分析 - 创建原始 bean 对象

    摘要:你也会了解到构造对象的两种策略。构造方法参数数量低于配置的参数数量,则忽略当前构造方法,并重试。通过默认构造方法创建对象看完了上面冗长的逻辑,本节来看点轻松的吧通过默认构造方法创建对象。 1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续。在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程。本篇文章,我们就从战术的层面上,详细分析doCreat...

    sutaking 评论0 收藏0

发表评论

0条评论

SKYZACK

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<