具有了技术深度,遇到问题能够快速定位并从根本上解决。有了技术深度以后,学习其它技术能够更快,再深刻其它技术也就不会惧怕java
【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、PropertyEditor面试
【小家Spring】聊聊Spring中的数据绑定 --- 属性访问器PropertyAccessor和实现类DirectFieldAccessor的使用
【小家Spring】聊聊Spring中的数据绑定 --- BeanWrapper以及Java内省Introspector和PropertyDescriptorspring
书写此篇博文的原因是出自一道面试题:面试题目大概如标题所述。
我我的认为这道面试题问得是很是有水平的,由于它涉及到的知识点既有深度,又有广度,可谓一箭双雕~~~所以在这里分享给你们。架构
为了给此文作铺垫,前面已经有两篇文章分别叙述了Java内省和BeanWrapper
,并且还分析了底层接口:属性访问器(PropertyAccessor
)。若对此部分还不是很了解的话,建议能够先出门左拐或者单击【相关阅读】里的连接~app
Spring须要依赖注入就须要使用BeanWrapper
,上章节说了BeanWrapperImpl
的实现大都委托给了CachedIntrospectionResults
去完成,而CachedIntrospectionResults
它的核心说法就是Java内省机制。框架
从层层委托的依赖关系能够看出,Spring IoC
的依赖注入(给属性赋值)是层层委托的最终给了Java内省机制,这是Spring框架设计精妙处之一。这也符合我上文所诉:BeanWrapper
这个接口并不建议应用本身去直接使用~~~
那么本文就着眼于此,结合源码去分析Spring IoC容器它使用BeanWrapper
完成属性赋值(依赖注入)之精华~函数
BeanWrapper
源码分析Spring IoC
我相信小伙伴并不陌生了,但IoC
的细节不是本文的重点。为了便于分析,我把这个过程画一个时序图描述以下:
有了这个简略的时序图,接下来就一步一步的分析吧源码分析
任何建立Bean的过程,都得经历doCreateBean()
。这句代码咱们已经很是熟悉了,它在AbstractAutowireCapableBeanFactory
里:post
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()
这个方法,它的做用是完成给属性赋值,从时序图中也能够看出这是一个入口学习
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 = (MutablePropertyValues) pvs; // 此处有个短路处理: // 若该mpvs中的全部属性值都已经转换为对应的类型,则把mpvs设置到BeanWrapper中,返回 if (mpvs.isConverted()) { // Shortcut: use the pre-converted values as-is. 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()); } // 显然,若调用者没有自定义转换器,那就使用BeanWrapper自己~~~(由于BeanWrapper实现了TypeConverter 接口~~) TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } // 获取BeanDefinitionValueResolver,该Bean用于将bean定义对象中包含的值解析为应用于目标bean实例的实际值。 BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter); // Create a deep copy, resolving any references for values. // 此处翻译成深度拷贝不合适,倒不如翻译成深度解析更为合理~~~~ List<PropertyValue> deepCopy = new ArrayList<>(original.size()); boolean resolveNecessary = false; // 遍历没有被解析的original属性值们~~~~ for (PropertyValue pv : original) { if (pv.isConverted()) { deepCopy.add(pv); } else { // 那种还没被解析过的PropertyValue此处会一步步解析~~~~ String propertyName = pv.getName(); // 属性名称 Object originalValue = pv.getValue(); // 未经类型转换的值(注意:是未经转换的,可能还只是个字符串或者表达式而已~~~~) // 最为复杂的解析逻辑~~~ Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); Object convertedValue = resolvedValue; // 属性可写 而且 不是嵌套(如foo.bar,java中用getFoo().getBar()表示)或者索引(如person.addresses[0])属性 boolean convertible = bw.isWritableProperty(propertyName) && !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName); if (convertible) { // 用类型转换器进行转换 convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter); } if (resolvedValue == originalValue) { if (convertible) { pv.setConvertedValue(convertedValue); } deepCopy.add(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)); } } } // 标记mpvs已经转换 if (mpvs != null && !resolveNecessary) { mpvs.setConverted(); } // Set our (possibly massaged) deep copy. // 使用转换后的值进行填充~~~~~~~~~~ try { bw.setPropertyValues(new MutablePropertyValues(deepCopy)); } catch (BeansException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex); } } // 属性值的转换 @Nullable private Object convertForProperty(@Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) { // 须要特别注意的是:convertForProperty方法是BeanWrapperImpl的实例方法,并不是接口方法 // 这个方法内部就用到了CachedIntrospectionResults,从何就和Java内省搭上了关系~~~ if (converter instanceof BeanWrapperImpl) { return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName); } else { // 自定义转换器 PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam); } }
说明:BeanDefinitionValueResolver
是Spring一个内建的非public
类,它在上述步骤中承担了很是多的任务,具体可参考此处:BeanDefinitionValueResolver和PropertyValues
从命名中就能看出,它处理BeanDefinition
的各式各样的状况,它主要是在xml
配置时代起到了很是大的做用,形如这样:
<bean class="foo.bar.xxx"> <property name="referBeanName" ref="otherBeanName" /> </bean>
由于咱们知道在xml
时代配置Bean很是的灵活:引用Bean、Map、List甚至支持SpEL等等,这一切权得益于BeanDefinitionValueResolver
这个类来处理各类case~
其实在如今注解大行其道的今天,配置Bean咱们大都使用@Bean
来配置,它是一种工厂方法的实现,所以这个处理类的做用就被弱化了不少。可是,可是,可是,它仍旧是咱们实施定制化BeanDefinition的一个有力武器~
applyPropertyValues()
这一步完成以后,就完全完成了对Bean实例属性的赋值。从中能够看到最终的赋值操做,核心依赖的就是这么一句话:
bw.setPropertyValues(new MutablePropertyValues(deepCopy))
而且从转换的逻辑咱们也须要知道的是:IoC并非100%得使用BeanWrapper
的,若咱们是自定义了一个转换器,实际上是能够不通过Java内省机制,而是直接经过反射来实现的,固然并不建议这么去作~
BeanWrapper
体系相比于 Spring 中其余体系是比较简单的,它做为BeanDefinition
向 Bean
转换过程当中的中间产物,承载了 bean 实例的包装、类型转换、属性的设置以及访问等重要做用(请不要落了访问这个重要能力)。
关于此面试题怎么去回答,若是是我主考我会这么评价回答:
populateBean()
这里算是对这块知识入门了applyPropertyValues()
这里,那基本对此回答就比较满意了固然若能答到:经过自定义实现一个转换器+反射实现做为实现,而绕过Java内省机制。那势必就能够加分了~
1. 若达到自定义、个性化定义BeanDefinition
这块(虽然与本问题没有必然关联),也是能够加分的(毕竟是面试而非考试~)
若文章格式混乱,可点击
:原文连接-原文连接-原文连接-原文连接-原文连接
==The last:若是以为本文对你有帮助,不妨点个赞呗。固然分享到你的朋友圈让更多小伙伴看到也是被做者本人许可的~
==
若对技术内容感兴趣能够加入wx群交流:Java高工、架构师3群
。
若群二维码失效,请加wx号:fsx641385712
(或者扫描下方wx二维码)。而且备注:"java入群"
字样,会手动邀请入群