References
前言
BeanPostProcessor
和BeanFactoryPostProcessor
是 Spring IoC Container 的两个重要的核心扩展点;基于 Annotation 注解的注入方式就是通过扩展BeanPostProcessor
实现的,所以这两个接口在 Spring IoC 容器中异常的重要;
本文为作者的原创作品,转载需注明出处;
源码分析环境搭建
参考 Spring Core Container 源码分析一:环境准备
BeanPostProcessor
在容器实例化 Bean 的前后可以通过BeanPostProcessor
接口注入相关的逻辑,但注意的是,它是在一个已经创建好的 bean 实例的前后注入逻辑,并不会影响到 bean 实例本身创建的逻辑;有关详细的功能介绍参考官方文档相关章节 Customizing beans using a BeanPostProcessor。
接口分析
1 | /** |
从类的注解上我们可以看到,它是针对已经instantiated
的 beans 进行的回调,也就是说是对实例化
好以后的 beans 进行的回调;总共提供了两个接口方法 postProcessBeforeInitialization 和 postProcessAfterInitialization,分别是在初始化
既initialized
beans 之前和之后调用的;具体调用过程以及实例化
和初始化
的区别参考 bean post processors 回调过程
测试用例
这里沿用Spring Core Container 源码分析二:Spring Beans 初始化流程分析中所使用到的测试用例,在这里,我们新增一个实现了 PostBeanProcessor 接口的实现类 InstantiationTracingBeanPostProcessor.java,以实现一个自定义的 bean-post-processor
1 | package org.shangyang.spring.container; |
beans.xml
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
直接将InstantiationTracingBeanPostProcessor
当做普通 bean 一样进行注册,Spring 容器会自动的检查该 bean 的类型,如果发现它是BeanPostProcessor
实例,那么会将其注册为 bean-post-processor 来进行处理;容器初始化了两个普通 bean,john 和 jane;期望在两个 bean 实例化以后能够织入 postProcessAfterInitialization 方法所实现的逻辑。
启动容器1
2
3
4
5
6"resource") (
public void testPostBeanProcessor(){
new ClassPathXmlApplicationContext("beans.xml");
}
运行结果1
2Bean 'jane' created : org.shangyang.spring.container.Person@591f989e
Bean 'john' created : org.shangyang.spring.container.Person@66048bfd
可见,成功的通过BeanPostProcessor
接口实现了对jane
和john
实例化过程的逻辑织入;那么它是如何做到的呢?看后面的源码分析。
源码分析
首先我们还是来看在分析Spring Beans 初始化流程中所使用到一张主流程图
如图,所有的bean-post-processors
将会在 bean-post-processors 实例化并注册步骤中进行实例化并注入 Spring Container 中;
instantiate and register bean-post-processor
该步骤对应的是流程图中被标注为bean-post-processors 实例化并注册
的步骤,AbstractApplicationContext#registerBeanPostProcessors 方法内部调用 PostProcessorRegistrationDelegate#registerBeanPostProcessors 完成这一动作;我们首先看看它的源码,
PostProcessorRegistrationDelegate.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
// 从 bean definitions 找出实现了 BeanPostProcessor 接口的类,
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// Register BeanPostProcessorChecker that logs an info message when
// a bean is created during BeanPostProcessor instantiation, i.e. when
// a bean is not eligible for getting processed by all BeanPostProcessors.
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
// Separate between BeanPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
// 下面的逻辑是将 BeanPostProcessors 根据实现了 PriorityOrdered,Ordered 接口和其它没有实现相应接口的实例进行分类;
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>();
List<String> orderedPostProcessorNames = new ArrayList<String>();
List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, register the BeanPostProcessors that implement PriorityOrdered.
// 对 priorityOrderedPostProcessors 进行排序,并注册到 BeanPostProcessors 中
sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
// Next, register the BeanPostProcessors that implement Ordered.
// 然后注册实现了 Ordered 接口的 BeanPostProcessors
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(beanFactory, orderedPostProcessors);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);
// Now, register all regular BeanPostProcessors.
// 现在,注册所有普通的 BeanPostProcessors
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
// Finally, re-register all internal BeanPostProcessors.
// 最后,注册所有的 internalPostProcessors
sortPostProcessors(beanFactory, internalPostProcessors);
registerBeanPostProcessors(beanFactory, internalPostProcessors);
// Re-register post-processor for detecting inner beans as ApplicationListeners,
// moving it to the end of the processor chain (for picking up proxies etc).
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
上述代码主要干了两件事情,第一件事:instantiate bean-post-processor;第二件事:classify and register bean-post-processors by order;下面,我们分别来分析这两个过程,
instantiate bean-post-processor
这里最最关键的地方是在代码第 21,44,57 行,他们其实不约而同的做了一件事情,就是实例化 bean-post-processor,
1 | BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); |
实例化过程参考Spring Core Container 源码分析二:Spring Beans 初始化流程分析中的 Do Get Bean 流程;
备注,
这些 bean-post-processors 是从什么地方找到的?见第一样代码1
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
根据 BeanPostProcessor 类型直接通过 beanFacotry 从 bean definitions 去查找。
classify and register bean-post-processors by order
从代码逻辑中,可以看到,根据 bean-post-processors 的不同分类,设置了不同的优先级; 然后根据不同的分类按其优先级顺序依次注入 Spring 容器中;bean-post-processors 被分为了四类,
实现了 PriorityOrdered 的 bean-post-processors 最先被注册,对应分类 priorityOrderedPostProcessors
实现了 Ordered 的 bean-post-processors 其次被注册,对应分类 orderedPostProcessorNames
没有实现任何排序接口的普通的 bean-post-processors 再其次被注册,对应分类 nonOrderedPostProcessorNames
最后注册 internal-bean-post-processors,对应分类 internalPostProcessors;
备注 interal-bean-post-processor 指的是实现了 MergedBeanDefinitionPostProcessor 接口的 bean-post-processor。
bean-post-processors 回调过程
由步骤 instantiate-and-register-bean-post-processor 我们得到了在容器中已经注册好的 bean-post-processors,那么这些 bean-post-processors 的回调接口方法是在什么时候被容器所调用的呢?
这个过程已经在 Spring Core Container 源码分析二:Spring Beans 初始化流程分析中的回调-bean-post-processors-接口方法章节中做了非常详细的分析,这里不再赘述;
Spring 内置的 bean-post-processors
这里举几个比较重要的 BeanPostProcessors
ApplicationContextAwareProcessor
该 bean-post-processor 回调主要是为 bean 注入当前的 ApplicationContext 容器实例,该部分的详细分析见注入 Aware 对象章节中的延生部分。BeanValidationPostProcessor
对 Bean 进行验证的回调方法。
总结(我的思考)
bean-post-processors 赋予了对原始 bean 无限的扩展可能,AspectJ 和 CGLib 就是典型的例子;
BeanFactoryPostProcessor
官方文档对其的描述参考 【7-8-2】Customizing configuration metadata with a BeanFactoryPostProcessor;他与 bean-post-processor 的相同点是,他们都参与实例化 bean 过程中的回调接口,但是,bean-post-processors 只能对已经实例化好的 bean 进行相关的修饰作用,而 bean-factory-post-processors 可以对 bean definitions 进行修改,也就是可以直接影响 bean 自身的实例化过程,所以,官方文档中一再强调,这个回调接口功能比较猛,一定要慎用;
补充,从名称上看,就可以知道,该 post processors 是对 BeanFactory 实例进行的操作;
接口分析
1 | /** |
从类的注解上可以看到,它允许对 bean definitions 也就是 bean 的配置进行修改,然后强调,一旦 bean-factory-post-processors 注入容器以后,ApplicationContext
可以自动感知;然后也限定了它的使用范围 A BeanFactoryPostProcessor may interact with and modify bean definitions, but never bean instances,这段注解清晰的说明了,bean-factory-post-processors 可以修改 bean definitions,但不能用于修改 bean instances;其实,bean-factory-post-processors 就是在 bean 实例化以前被调用,主要用来修改 bean definitions;
可以看到,该接口只有一个接口方法 void postProcessBeanFactory(beanFactory),有意思的是关于这个方法的注解,把它翻译一下,
Modify the application context’s internal bean factory after its standard initialization. All bean definitions will have been loaded, but no beans will have been instantiated yet. This allows for overriding or adding properties even to eager-initializing beans.
该方法是用来修改一个 ApplicationContext 内部的已经经过标准实例化的 bean factory;修改的时机是当所有的 bean definitions 都已经加载好了,但是还没有一个 beans 被实例化;这也就是使得它有能力覆盖或者添加属性,甚至于可以用来实例化一些 eager-beans;
从方法的注解上可以看到,修改 bean definitions 的行为主要是发生在对 bean factory 实例的上;后面,在源码剖析部分,将会有更多的描述;
测试用例
这里继续沿用 Spring Core Container 源码分析二:Spring Beans 初始化流程分析中所使用到的测试用例,在这里,我们使用一个内置的 factory-bean-post-processor PropertyPlaceholderConfigurer
来进行测试,它的父类实现了BeanFactoryPostProcessor
接口,主要目的是用来修改 bean definitions;
beans.properties
1 | john.name: John Doe |
创建一个 beans.properties 属性文件,并定义上述两个 property;
beans.xml
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
注入 factory-bean-post-processor PropertyPlaceholderConfigurer
到容器中,这样,它会作为回调方法参与 bean 的实例化过程。
工程目录结构
启动容器测试
1 |
|
_输出_
测试通过,表明,通过 beans.properties 中所定义的属性值成功的注入到了 john 和 jane 的 bean definitions 中;
源码分析
依然从 Spring Beans 初始化流程中所使用到一张主流程图来进行分析,
如图,红色注释为bean-factory-post-processors 实例化并执行
的步骤,正是 bean-factory-post-processors 实例化并被 Spring 容器执行的地方,我们从这里开始分析;
instantiate and process factory-bean-post-processor
该步骤是描述 bean-factory-post-processors 被实例化,然后被 Spring 容器处理的过程;
首先调用的是 AbstractApplicationContext#invokeBeanFactoryPostProcessors(beanFactory)
1 | /** |
从该方法的注解中我们可以看到,实例化并按照指定的 Order 顺序调用已注册的 bean-factory-post-processors;第 7 行代码是关键,该步骤里面实现了对 bean-factory-post-processors 的实例化和按照顺序注入 Spring 容器的逻辑;从第 11 行代码开始,要注意的是,分别设置了LoadTimeWeaverAwareProcessor
和ContextTypeMatchClassLoader
;我们主要关注第 7 行代码 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors,它是如何实现对 bean-factory-post-processors 实例化和注册的;
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<String>();
....
/** 上面的一大段逻辑都是在处理 BeanDefinitionRegistryPostProcessor 的回调接口 **/
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
List<String> orderedPostProcessorNames = new ArrayList<String>();
List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); // 注意哈,这里有调用了 DO Get Bean 流程去实例化一个 Bean
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
/** 很直白,下面就直接一次调用 bean-factory-post-processors 了.. Spring 的代码真该重构重构了... **/
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(beanFactory, orderedPostProcessors);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors.
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}
说实话,当看过这段在一个方法内部的超长的代码以后(当然,删除了大部分),给我的感觉是,Spring 容器里面的核心代码,也应该重构重构了.. 写的比较的冗余;删除了 BeanDefinitionRegistryPostProcessor 相关织入的逻辑,该部分代码是 Spring 在后期添加的,并不影响核心的逻辑;我们来分析这段代码的逻辑,
代码第 13 行,从 bean definitions 配置中得到相关的 bean-factory-post-processors name
代码第 18-34 行,根据 bean-factory-post-processors 所实现的不同的 Order 接口分类
实现了 PriorityOrdered 接口的放入 priorityOrderedPostProcessors
实现了 Ordered 接口的放入 orderedPostProcessorNames
没有实现任何 Order 接口的放入 nonOrderedPostProcessorNames实例化 bean-factory-post-processors,代码 26,43,51 行分别对不同分类的 bean-factory-post-processors 进行实例化,该实例化步骤参考 Do Get Bean 流程;
然后,代码 38, 46, 53 行按照优先级顺序分别
调用
bean-factory-post-processors 的回调接口1
2
3
4
5
6
7
8
9
10/**
* Invoke the given BeanFactoryPostProcessor beans.
*/
private static void invokeBeanFactoryPostProcessors(
Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanFactory(beanFactory);
}
}可以看到,依次遍历 bean-factory-post-processors,然后分别
调用
其回调接口,注意,这里是直接就调用
了次回调接口,与 bean-post-processors 不同,bean-post-processors 是先通过注册缓存到 bean factory 中,然后在普通 bean 的实例化过程调用;
上面就是整个 bean-factory-post-processors 被处理的主流程了;最后,来看看测试用例中所使用到的 PropertyPlaceholderConfigurer
PropertyPlaceholderConfigurer
PropertyPlaceholderConfigurer 的父类 PropertyResourceConfigurer 实现了 BeanFactoryPostProcessor 接口;
PropertyResourceConfigurer.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/**
* {@linkplain #mergeProperties Merge}, {@linkplain #convertProperties convert} and
* {@linkplain #processProperties process} properties against the given bean factory.
* @throws BeanInitializationException if any properties cannot be loaded
*/
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
Properties mergedProps = mergeProperties();
// Convert the merged properties, if necessary.
convertProperties(mergedProps);
// Let the subclass process the properties.
processProperties(beanFactory, mergedProps);
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
代码第 9、12 行,都是父类提供的方法,从指定的 properties 文件中获取自定义的 Properties,然后执行相关的 merge 操作,再 convert;有关这个操作的更多说明参考官方文档 Example: the PropertyOverrideConfigurer;代码第 15 行 processProperties(beanFactory, mergedProps); 是一个抽象的方法,是可以用来被子类继承的方法
PropertyResourceConfigurer.java1
2
3
4
5
6
7
8/**
* Apply the given Properties to the given BeanFactory.
* @param beanFactory the BeanFactory used by the application context
* @param props the Properties to apply
* @throws org.springframework.beans.BeansException in case of errors
*/
protected abstract void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
throws BeansException;
该方法的注解说得非常的清楚,将 properties apply to BeanFactory,既是将外部的 Properties 应用到 BeanFactory 对象实例中;下面来看看 PropertyPlaceholderConfigurer 对该方法的实现,
PropertyPlaceholderConfigurer.java1
2
3
4
5
6
7
8
9
10
11/**
* Visit each bean definition in the given bean factory and attempt to replace ${...} property
* placeholders with values from the given properties.
*/
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
throws BeansException {
StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
doProcessProperties(beanFactoryToProcess, valueResolver);
}
这里的逻辑就是描述 PropertyPlaceholderConfigurer 是如何通过 properties 文件中的定义内容去修改 bean definitions 的原始内容了的过程了;很清晰,通过 PlaceholderResolvingStringValueResolver 处理 properties 然后再将其通过方法 doProcessProperties 应用到 beanFactory 实例中;详细细节这里就不再深究了,有兴趣的读者可以继续深挖它的实现逻辑;
Spring 内置的 bean-factory-post-processors
上面就是目前 Spring Core Container 中所实现的所有内置的 bean-factory-post-processors,图中用箭头指出的便是我在本章节测测试用例中所使用到的PropertyPlaceholderConfigurer
,还有很多核心并且好玩的实现类,比如AspectJWeavingEnabler
、CustomAutowireConfigurer
、CustomEditorConfigurer
等等
总结(我的思考)
BeanFactoryPostProcessor
提供了一个无比强大的针对BeanFactory
实例的回调接口,从设计初衷上而言,主要是用来修改 bean definitions,进而影响 bean 的实例化过程;上述通过 PropertyPlaceholderConfigurer 给出的测试用例清晰的看到了这一点,我们可以通过外部 properties 文件的内容而影响到最终实例 john
和 jane
的姓名;我想说的是,这只是小试牛刀,我突然想到一个能够解决让所有从事 Spring J2EE 服务器端开发工程师都比较头疼的问题,就是数据库的秘钥往往都是以明文
的方式保存在配置文件中的,无论是使用 properties 或者是直接写在 XML 配置文件中,然而,当我们明白了BeanFactoryPostProcessor
的特性以后,实际上我们完全可以参照PropertyPlaceholderConfigurer
的做法自己去实现一个DecryptPropertyPlaceholderConfigurer
,将加密后的秘钥放在 properties 文件中,然后通过DecryptPropertyPlaceholderConfigurer
通过解密秘钥解密,这样,就可以做到,秘钥的加密保存策略,而不用再使用明文的方式保存在 properties 或者 xml 文件中了。另外,Spring Boot 也是通过BeanFactoryPostProcessor
实现了对 yml 格式的属性文件的解析;
写在最后
bean-factory-post-processors
在实例化 bean 之前,对实例化 bean 的 definitions 进行修改,进而影响到 bean 的实例化过程,可以算是对 bean 内部进行控制;而bean-post-processors
是对已经实例化好的 bean 进行修饰作用,可以算是对 bean 的外部控制;这样,bean 通过 Spring 容器的全方位的控制,可谓是内外兼修,这也是为什么 Spring 可以真正称得上一个 IoC 容器的原因;