-
死磕Spring之IoC篇 - Bean 的属性填充阶段
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读
Spring 版本:5.1.14.RELEASE
开始阅读这一系列文章之前,建议先查看《深入了解 Spring IoC(面试题)》这一篇文章
该系列其他文章请查看:《死磕 Spring 之 IoC 篇 - 文章导读》
Bean 的属性填充阶段
当我们显示或者隐式地调用AbstractBeanFactory
的 getBean(...)
方法时,会触发 Bean 的加载,在《开启 Bean 的加载》文章中分析了整个加载过程。
对于不同作用域的 Bean,底层都会调用 AbstractAutowireCapableBeanFactory
的 createBean(...)
方法进行创建,在《Bean 的创建过程》文章中分析了整个创建过程。创建 Bean 的过程中,在获取到的一个实例对象后,里面的相关属性也许是空的,那么接下来要做的就是将需要填充的属性进行依赖注入,然后再进行后续的初始化工作。整个的属性填充过程非常复杂,因为配置的属性值可能是一个表达式,需要解析,类型也可能不对,需要进行类型转换,还可能是一个对象,需要找到对应的 Bean 然后注入(依赖注入),存在有各种处理,本文将会分析创建 Bean 过程中的属性填充阶段。
回顾
先来回顾一下创建 Bean 过程中属性填充阶段对应的代码:
在创建好实例对象后,这个对象的属性还没有赋值,所以将这个实例对象的相关属性进行赋值,也就是上面的第 <4>
步
开启 Bean 的属性填充
populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)
方法,属性填充,如下:
过程大致如下:
-
如果实例对象为
null
,则进行下面的判断- 这个 Bean 有属性,则抛出异常
-
否则,不用属性填充,直接
return
-
实例化阶段的后置处理,如果满足这两个条件:RootBeanDefinition 不是用户定义的(由 Spring 解析出来的)、是否有 InstantiationAwareBeanPostProcessor 处理器
-
遍历所有的 BeanPostProcessor
-
如果为 InstantiationAwareBeanPostProcessor 类型,则对实例化对象进行后置处理
注意,如果返回 false,直接
return
,不会调用后面的 InstantiationAwareBeanPostProcessor 处理器,也不会进行接下来的属性填充
-
-
获取
pvs
,承载当前对象的属性值 -
获取这个 Bean 的注入模式,默认为 AUTOWIRE_NO,例如可以通过
@Bean
注解的autowire
属性配置注入模式- 如果注入模式为 AUTOWIRE_BY_NAME 或者 AUTOWIRE_BY_TYPE,则通过下面的方式获取属性值
-
将
pvs
封装成 MutablePropertyValues 对象newPvs
(允许对属性进行相关操作) -
AUTOWIRE_BY_NAME 模式,通过名称获取相关属性值,保存在
newPvs
中,调用autowireByName(...)
方法 -
AUTOWIRE_BY_TYPE 模式,通过类型获取相关属性值,保存在
newPvs
中,调用autowireByType(...)
方法 -
将
newPvs
复制给pvs
-
通过 InstantiationAwareBeanPostProcessor 处理器(如果有)对
pvs
进行处理- 遍历所有的 BeanPostProcessor
-
如果为 InstantiationAwareBeanPostProcessor 类型,则调用其
postProcessProperties(...)
方法,对pvs
进行后置处理 -
如果上一步的处理结果为空,可能是新版本导致的(Spring 5.1 之前没有上面这个方法),则需要兼容老版本
-
尝试找到这个 Bean 的所有
java.beans.PropertyDescriptor
属性描述器(包含这个属性的所有信息) -
调用处理器的
postProcessPropertyValues(...)
方法,对pvs
进行后置处理 -
如果处理后的 PropertyValues 对象为空,直接
return
,则不会调用后面的处理器,也不会进行接下来的属性填充
-
尝试找到这个 Bean 的所有
-
将处理后的
pvsToUse
复制给pvs
-
依赖检查
-
找到这个 Bean 的所有
java.beans.PropertyDescriptor
属性描述器(包含这个属性的所有信息) - 进行依赖检查,如果没有找到对应的属性值,则根据检查策略进行抛出异常(默认不会)
-
找到这个 Bean 的所有
-
如果
pvs
不为空,则将里面的属性值设置到当前 Bean 对应的属性中(依赖注入),调用applyPropertyValues(...)
方法前面找到的属性值并没有设置到 Bean 中,且属性值可能是一个表达式,类型也可能也不对,需要先进行处理和类型转换,然后再设置到该实例对象中
整个的属性填充过程非常的复杂,接下来进行概括:
-
允许你对实例化对象进行后置处理,处理结果为
false
表示不需要进行接下来的属性填充过程 - 根据注入模式,找到没有配置属性值的对象属性,然后找到对应的 Bean,默认注入模式为不注入
-
允许你对属性值进行后置处理,例如
@Autowired
、@Value
等注解标注的属性会通过这里找到对应的属性值(或对象) - 上述过程仅找到了属性值,还没设置到当前实例对象中,所以最后一步才是真正的属性填充
上面有两种注入模式:AUTOWIRE_BY_NAME 和 AUTOWIRE_BY_TYPE,默认为 AUTOWIRE_NO,接下来先来看看这两种注入模式的实现
通过名称获取属性值
autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs)
方法,通过名称获取相关属性值,如下:
过程并不复杂,大致如下:
-
获取当前 Bean 中不满意的非简单类型的属性名称,也就是没有定义属性值的"对象"属性,如下:
-
遍历这些对象属性的名称
-
如果当前容器存在对应的 Bean(通过名称判断)
-
根据属性名称获取对应的
bean
对象(依赖查找) -
将
bean
添加至pvs
- 将两个 Bean 之间的依赖关系保存起来
-
根据属性名称获取对应的
直接根据"对象"名称通过 getBean(String beanName)
获取到对应的对象(依赖查找)
通过类型获取属性值
autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs)
方法,通过类型获取相关属性值,如下:
过程大致如下:
- 获取 TypeConverter 类型转换器,用于取代默认的 PropertyEditor 类型转换器
- 获取当前 Bean 中不满意的非简单类型的属性名称,也就是没有定义属性值的"对象"属性,和通过名称注入的过程一样
-
遍历这些"对象"属性的名称,获取这个属性的
java.beans.PropertyDescriptor
属性描述器(包含这个属性的所有信息) - 如果不是 Object 类型(对 Object 类类型的 Bean 进行自动装配毫无意义),则尝试找到对应的对象
- 找到这个属性的写方法
- 创建对应的 DependencyDescriptor 依赖注入描述对象,默认 required 为 false,表示找不到也没关系
-
依赖注入,找到该属性对应的对象,调用
resolveDependency(...)
方法 -
如果找到属性对象,则将该其添加至
pvs
- 将注入的属性对象和当前 Bean 之前的关系保存起来
根据"对象"名称通过 resolveDependency(...)
获取到对应的对象,该方法就是依赖注入的底层实现,整个过程也非常复杂,所以将这部分内容放在下一篇《@Autowired 等注解的实现原理》文章中
属性值的后置处理
调用 InstantiationAwareBeanPostProcessor#postProcessProperties
方法,对前面属性值进行处理
在前面的AUTOWIRE_BY_NAME 和 AUTOWIRE_BY_TYPE两种注入模式中,找到的都是普通对象的属性值,例如 @Autowired、@Value 和 @Resource 注解并没有被解析,且默认的注入模式还是AUTOWIRE_NO,那这些注解是如何被解析的呢?Spring 内部有下面两个 InstantiationAwareBeanPostProcessor 处理器:
- AutowiredAnnotationBeanPostProcessor,解析 @Autowired 和 @Value 注解标注的属性,获取对应属性值
- CommonAnnotationBeanPostProcessor,会解析 @Resource 注解标注的属性,获取对应的属性值
这里先提一下,具体的实现过程在下一篇《@Autowired 等注解的实现原理》文章中进行分析
属性填充
applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs)
方法,属性填充
如果获取到的 pvs
属性值对象不为空,则将里面的属性值设置到当前 Bean 对应的属性中(依赖注入),我们知道前面找到的属性值并没有设置到 Bean 中,且属性值可能是一个表达式,类型也可能也不对,需要先进行处理和类型转换,然后再设置到该实例对象中,方法如下:
过程大致如下:
-
没有相关属性值,则直接
return
返回
开始属性值的转换与填充,先定义一个 original
集合,承载属性值(未进行转换)
-
如果
pvs
是 MutablePropertyValues 类型,则可能已经处理过了,否则,获取所有的属性值集合,放入original
集合中-
属性值已经转换了,则将这些属性值设置到当前 Bean 中(反射机制),依赖注入的最终实现!!!
调用
BeanWrapperImpl#setPropertyValues(PropertyValues)
方法 -
没有转换过,则获取所有的属性值集合,放入
original
集合中
-
-
定义一个
deepCopy
集合,保存转换后的属性值 -
遍历所有的属性值,进行转换(如果有必要)
-
已经转换过,则直接添加到
deepCopy
中 -
否则,开始进行转换
-
表达式的处理(如果有必要的话),例如你在 XML 配置的属性值为
${systenm.user}
,则会解析出对应的值 - 使用类型转换器转换属性值(如果有必要的话)
- 设置转换后的值,避免上面的各种判断
-
添加到
deepCopy
中
-
表达式的处理(如果有必要的话),例如你在 XML 配置的属性值为
-
已经转换过,则直接添加到
-
如果属性值不为空,且不需要每次都处理,则设置为已转换
-
将属性值设置到当前 Bean 中(反射机制),依赖注入的最终实现!!!
调用
BeanWrapperImpl#setPropertyValues(PropertyValues)
方法
整个属性注入过程非常复杂,上面仅列出了关键步骤,可以看到最终会调用 BeanWrapperImpl#setPropertyValues(PropertyValues)
方法将属性值设置到 Bean 中
在 Bean 的实例化阶段获取到的就是一个 BeanWrapperImpl 对象,所以这里调用的就是当前 Bean 的 setPropertyValues(PropertyValues)
方法,该方法的底层借助于 Java Beans 的 java.beans.PropertyDescriptor
属性描述器,获取到对应的写方法,然后通过反射机制设置当前 Bean 的属性值
总结
当我们显示或者隐式地调用AbstractBeanFactory
的 getBean(...)
方法时,会触发 Bean 的加载,在《开启 Bean 的加载》文章中分析了整个加载过程。
对于不同作用域的 Bean,底层都会调用 AbstractAutowireCapableBeanFactory
的 createBean(...)
方法进行创建,在《Bean 的创建过程》文章中分析了整个创建过程。创建 Bean 的过程中,在获取到的一个实例对象后,需要获取相关属性值,然后注入到 Bean 中,其中获取属性值有三种模式:
-
AUTOWIRE_NO,默认,不获取相关属性值
-
AUTOWIRE_BY_NAME,通过名称获取没有定义属性值的"对象"的属性值,通过
getBean(String beanName)
查找 -
AUTOWIRE_BY_TYPE,通过类型获取没有定义属性值的"对象"的属性值,依赖注入的方式
默认情况下,获取到已定义的属性值后不会通过上面的方式去找属性值,在后续有一个属性值的后置处理,会调用所有的 InstantiationAwareBeanPostProcessor 处理器的 postProcessProperties 方法进行处理,例如 Spring 内部有两个 InstantiationAwareBeanPostProcessor 处理器:
- AutowiredAnnotationBeanPostProcessor,解析 @Autowired 和 @Value 注解标注的属性,获取对应属性值
- CommonAnnotationBeanPostProcessor,会解析 @Resource 注解标注的属性,获取对应的属性值
在获取到所有的属性值后然后通过反射机制设置到 Bean 中
关于 @Autowired、@Value 和 @Resource 注解的实现原理将在下一篇《@Autowired 等注解的实现原理》文章中进行分析
__EOF__