每篇一句
具备了技术深度,遇到问题可以快速定位并从根本上解决。有了技术深度之后,学习其它技术可以更快,再深入其它技术也就不会害怕
相关阅读
【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、PropertyEditor
【小家Spring】聊聊Spring中的数据绑定 --- 属性访问器PropertyAccessor和实现类DirectFieldAccessor的使用
【小家Spring】聊聊Spring中的数据绑定 --- BeanWrapper以及Java内省Introspector和PropertyDescriptor
前言
书写此篇博文的缘由是出自一道面试题:面试题目大概如标题所述。
我个人认为这道面试题问得是非常有水平的,因为它涉及到的知识点既有深度,又有广度,可谓一举两得~~~因此在这里分享给大家。
为了给此文做铺垫,前面已经有两篇文章分别叙述了Java内省和BeanWrapper
,而且还分析了底层接口:属性访问器(PropertyAccessor
)。若对此部分还不是很了解的话,建议可以先出门左拐或者单击【相关阅读】里的链接~
Spring IoC和Java内省的依赖关系说明
Spring需要依赖注入就需要使用BeanWrapper
,上章节说了BeanWrapperImpl
的实现大都委托给了CachedIntrospectionResults
去完成,而CachedIntrospectionResults
它的核心说法就是Java内省机制。
从层层委托的依赖关系可以看出,Spring IoC
的依赖注入(给属性赋值)是层层委托的最终给了Java内省机制,这是Spring框架设计精妙处之一。这也符合我上文所诉:BeanWrapper
这个接口并不建议应用自己去直接使用~~~
那么本文就着眼于此,结合源码去分析Spring IoC容器它使用BeanWrapper
完成属性赋值(依赖注入)之精华~
Spring IoC中使用BeanWrapper
源码分析
Spring IoC
我相信小伙伴并不陌生了,但IoC
的细节不是本文的重点。为了便于分析,我把这个过程画一个时序图描述如下:
有了这个简略的时序图,接下来就一步一步的分析吧
doCreateBean() 创建Bean
任何创建Bean的过程,都得经历doCreateBean()
。这句代码我们已经非常熟悉了,它在AbstractAutowireCapableBeanFactory
里:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
...
// 这一步简单的说:通过构造函数实例化Bean后,new BeanWrapperImpl(beanInstance)包装起来
// 并且:initBeanWrapper(bw); 作用是注册ConversionService和registerCustomEditors() ...
instanceWrapper = createBeanInstance(beanName, mbd, args);
...
// 给属性赋值:此处会实施BeanWrapper的真正实力~~~~
// 注意:此处第三个参数传入的是BeanWrapper,而不是源生beanduixiang~~~
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
...
}
doCreateBean
这个方法完成整个Bean的实例化、初始化。而这里面我们最为关注的自然就是populateBean()
这个方法,它的作用是完成给属性赋值,从时序图中也可以看出这是一个入口
populateBean():给Bean的属性赋值~
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
...
// 从Bean定义里面把准备好的值都拿出来~~~
// 它是个MutablePropertyValues,持有N多个属性的值~~~
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
...
for (BeanPostProcessor bp : getBeanPostProcessors()) {
...
// 此处会从后置处理,从里面把依赖的属性,值都拿到。比如大名鼎鼎的AutowiredAnnotationBeanPostProcessor就是在此处拿出值的~~~
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
...
pvs = pvsToUse;
}
...
// 若存在属性pvs ,那就做赋值操作吧~~~(本处才是今天关心的重点~~~)
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
深入到方法内部,它完成了k-v值的准备工作,很多重要的BeanPostProcessor
也在此处得到执行。对于最终给属性赋值的步骤,是交给了本类的applyPropertyValues()
方法去完成~~~
其实到了此处,理论上小伙伴就应该就能猜到接下来的核心下文了~
applyPropertyValues()
:完成属性赋值
这个方法的处理内容才是本文最应该关注的核心,它在处理数据解析、转换这一块还是存在不小的复杂度的~
// 本方法传入了beanName和bean定义信息,以及它对应的BeanWrapper和value值们~
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
if (pvs.isEmpty()) {
return;
}
...
MutablePropertyValues mpvs = null;
List<PropertyValue> original;
// 说明一下:为何这里还是要判断一下,虽然Spring对PropertyValues的内建实现只有MutablePropertyValues
// 但是这个是调用者自己也可以实现逻辑的~~~so判断一下最佳~~~~
if (pvs instanceof MutablePropertyValues) {
mpvs = (Mutable