BeanFactoryPostProcessors
介绍
BeanFactoryPostProcessors完整定义:
|
|
我们知道spring最大优点就是其可扩展性,BeanFactoryPostProcessor接口就是spring中提供给我们用于扩展的一个地方。我们看该接口上的javadoc其实非常的详细,这也是我们看spring源码的一个技巧,就是看一个类是干嘛的一定要先通读其注释。
|
|
我们可以看一下ApplicationContext中BeanFactoryPostProcessor的调用位置来印证是否如此
下面是ApplicationContext核心代码:
经过
这两步我们已经拿到了beanFactory实例,也就是每一个bean对应的BeanDefinition已经加载好了。下面才是执行invokeBeanFactoryPostProcessors(beanFactory),也就印证了我们上面的结论。
下面我们通过一个BeanFactoryPostProcessor的典型应用PropertyPlaceholderConfigurer来详细讲解BeanFactoryPostProcessor执行原理
PropertyPlaceholderConfigurer
简单使用的例子
PropertyPlaceholderConfigurer相信大家都使用过,我们在配置bean的属性可以使用占位符来赋值,然后通过调整properties文件中对应的属性值来修改。看一个使用PropertyPlaceholderConfigurer简单的例子:
bean.properties配置文件:
单元测试:
输出结果一切正常:
|
|
源码分析
先看一下PropertyPlaceholderConfigurer的类继承图:
可以看到PropertyPlaceholderConfigurer实现了BeanFactoryPostProcessor和 PriorityOrdered。
我们接着对上面的 invokeBeanFactoryPostProcessors(beanFactory)继续进行分析:
上面主要的执行逻辑我都添加了中文注释方便大家理解。
总结一下改方法主要做的事情:
- 先判断如果beanFactory是BeanDefinitionRegistry类型的话就添加对BeanDefinitionRegistryPostProcessor类型的调用,BeanDefinitionRegistryPostProcessor接口是BeanFactoryPostProcessor的子接口,BeanDefinitionRegistryPostProcessor的作用是方便我们可以手动注册bean交给spring来管理,可以通过扩展其唯一的方法(void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;) 来注册bean到spring里,这个不是本次分析的重点后面再举例讲解。
- 先对硬编码配置的BeanFactoryPostProcessor进行处理 如果是BeanDefinitionRegistryPostProcessor类型会进行postProcessBeanDefinitionRegistry调用和postProcessBeanFactory调用,如果不是则只进行postProcessBeanFactory调用。
- 再对配置的BeanDefinitionRegistryPostProcessor进行处理(postProcessBeanDefinitionRegistry调用和postProcessBeanFactory调用)
- 最后对配置BeanFactoryPostProcessor的进行处理会按 PriorityOrdered/Ordered/没有继承任何排序接口 三种进行优先级排序执行postProcessBeanFactory调用。**其中我们的PropertyPlaceholderConfigurer 实现了BeanFactoryPostProcessor和 PriorityOrdered,所以会在这一步执行调用
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);**
- 我们继续进行分析:
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
会执行PropertyPlaceholderConfigurer父类PropertyResourceConfigurer中的方法
|
|
mergeProperties()方法会先将
后面调用 processProperties(beanFactory, mergedProps);进行处理
- processProperties(beanFactory, mergedProps)分析
|
|
这里只有两行代码,第一行是创建StringValueResolver实例(用于替换占位符的真正方法)
我们跨过千山万水终于要到马上要进行占位符替换了,继续分析
|
|
上面我已经使用中文注释写的很清楚了接着进行分析
- visitor.visitBeanDefinition(bd);
|
|
这里可以对应 bean的parentName beanClassName property等进行替换操作我们这里只关注属性的替换操作
- visitPropertyValues(beanDefinition.getPropertyValues());
|
|
- Object newVal = resolveValue(pv.getValue());
|
|
这里有很多类型,是因为spring支持很多类型的配置比如property的值我们可以配置为ref=xxxbean那么value就是RuntimeBeanReference类型,
如果配置
那么value就是List类型等等。这里我们例子中配置的类型是TypedStringValue,那么执行
|
|
- String visitedString = resolveStringValue(stringValue);
|
|
valueResolver其实就是PlaceholderResolvingStringValueResolver实例,它又委托PropertyPlaceholderHelper进行操作
也就是
该方法是真正替换的操作所在,改方法已经添加中文注释应该很好理解了,总结就是做了一下两件事:
1.首先会将ex. ${student.name} 转成 student.name
2.将student.name通过 tring propVal = placeholderResolver.resolvePlaceholder(placeholder);获取真正的值然后返回。
|
|
该方法就比较简单了就是根据不同的模式做处理,systemPropertiesMode默认是SYSTEM_PROPERTIES_MODE_FALLBACK
- 如果systemPropertiesMode = SYSTEM_PROPERTIES_MODE_OVERRIDE 是指 系统配置文件优先级大于我们的配置文件。
- 拿不到或者配置不等于SYSTEM_PROPERTIES_MODE_OVERRIDE 就去我们配置文件里进行获取
- 如果还获取不到且如果systemPropertiesMode=SYSTEM_PROPERTIES_MODE_FALLBACK就再尝试去系统文件里查找。
结论
经过以上各个步骤最终BeanDefinition里的parentName beanClassName property中的占位符都会被我们propertis配置文件中对应的值所替换掉,这就为后续实例化bean后做bean实例属性填充时做好了准备。
我们再进行 Student student =(Student) ac.getBean(“student”); 时就可以正常打印对应的值了。
看源码的一点分享tip
- 看源码可能会比较枯燥,坚持很重要,如果看一遍是懵的那就是歇一歇再继续多看几遍这样慢慢就会找到感觉
- 看的过程中一定要写例子进行调试,然后再继续反复看慢慢就会明白其中的含义
- 看源码可以看某一个核心类的继承图,以及javadoc。还可以尝试画出核心的时序图,这些都能对我们看懂源码起到事半功倍的作用。
至此本篇源码分析就结束了,后续继续spring源码分析的文章。加油!!!