Spring Core Container 源码分析四:BeanPostProcessor 和 BeanFactoryPostProcessor

References

前言

BeanPostProcessorBeanFactoryPostProcessor是 Spring IoC Container 的两个重要的核心扩展点;基于 Annotation 注解的注入方式就是通过扩展BeanPostProcessor实现的,所以这两个接口在 Spring IoC 容器中异常的重要;

本文为作者的原创作品,转载需注明出处;

源码分析环境搭建

参考 Spring Core Container 源码分析一:环境准备

BeanPostProcessor

在容器实例化 Bean 的前后可以通过BeanPostProcessor接口注入相关的逻辑,但注意的是,它是在一个已经创建好的 bean 实例的前后注入逻辑,并不会影响到 bean 实例本身创建的逻辑;有关详细的功能介绍参考官方文档相关章节 Customizing beans using a BeanPostProcessor

接口分析

1
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
/**
* Factory hook that allows for custom modification of new bean instances,
* e.g. checking for marker interfaces or wrapping them with proxies.
*
* <p>ApplicationContexts can autodetect BeanPostProcessor beans in their
* bean definitions and apply them to any beans subsequently created.
* Plain bean factories allow for programmatic registration of post-processors,
* applying to all beans created through this factory.
*
* <p>Typically, post-processors that populate beans via marker interfaces
* or the like will implement {@link #postProcessBeforeInitialization},
* while post-processors that wrap beans with proxies will normally
* implement {@link #postProcessAfterInitialization}.
*
* @author Juergen Hoeller
* @since 10.10.2003
* @see InstantiationAwareBeanPostProcessor
* @see DestructionAwareBeanPostProcessor
* @see ConfigurableBeanFactory#addBeanPostProcessor
* @see BeanFactoryPostProcessor
*/
public interface BeanPostProcessor {

/**
* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

/**
* Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding {@code bean instanceof FactoryBean} checks.
* <p>This callback will also be invoked after a short-circuiting triggered by a
* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other BeanPostProcessor callbacks.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.FactoryBean
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

从类的注解上我们可以看到,它是针对已经instantiated的 beans 进行的回调,也就是说是对实例化好以后的 beans 进行的回调;总共提供了两个接口方法 postProcessBeforeInitialization 和 postProcessAfterInitialization,分别是在初始化initialized beans 之前和之后调用的;具体调用过程以及实例化初始化的区别参考 bean post processors 回调过程

测试用例

这里沿用Spring Core Container 源码分析二:Spring Beans 初始化流程分析中所使用到的测试用例,在这里,我们新增一个实现了 PostBeanProcessor 接口的实现类 InstantiationTracingBeanPostProcessor.java,以实现一个自定义的 bean-post-processor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.shangyang.spring.container;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor{

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 这里的扩展点实现很简单,只是打印出 bean 的名称
System.out.println("Bean '" + beanName + "' created : " + bean.toString());

return bean;
}

}

beans.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- 传统的方式 -->
<bean name="john" class="org.shangyang.spring.container.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>

<bean name="jane" class="org.shangyang.spring.container.Person">
<property name="name" value="Jane Doe"/>
</bean>

<bean class="org.shangyang.spring.container.InstantiationTracingBeanPostProcessor"/>

</beans>

直接将InstantiationTracingBeanPostProcessor当做普通 bean 一样进行注册,Spring 容器会自动的检查该 bean 的类型,如果发现它是BeanPostProcessor实例,那么会将其注册为 bean-post-processor 来进行处理;容器初始化了两个普通 bean,johnjane;期望在两个 bean 实例化以后能够织入 postProcessAfterInitialization 方法所实现的逻辑。

启动容器

1
2
3
4
5
6
@SuppressWarnings("resource")
@Test
public void testPostBeanProcessor(){

new ClassPathXmlApplicationContext("beans.xml");
}

运行结果

1
2
Bean 'jane' created : org.shangyang.spring.container.Person@591f989e
Bean 'john' created : org.shangyang.spring.container.Person@66048bfd

可见,成功的通过BeanPostProcessor接口实现了对janejohn实例化过程的逻辑织入;那么它是如何做到的呢?看后面的源码分析。

源码分析

首先我们还是来看在分析Spring Beans 初始化流程中所使用到一张主流程图

如图,所有的bean-post-processors将会在 bean-post-processors 实例化并注册步骤中进行实例化并注入 Spring Container 中;

instantiate and register bean-post-processor

该步骤对应的是流程图中被标注为bean-post-processors 实例化并注册的步骤,AbstractApplicationContext#registerBeanPostProcessors 方法内部调用 PostProcessorRegistrationDelegate#registerBeanPostProcessors 完成这一动作;我们首先看看它的源码,

PostProcessorRegistrationDelegate.java

1
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
73
public 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 被分为了四类,

  1. 实现了 PriorityOrdered 的 bean-post-processors 最先被注册,对应分类 priorityOrderedPostProcessors

  2. 实现了 Ordered 的 bean-post-processors 其次被注册,对应分类 orderedPostProcessorNames

  3. 没有实现任何排序接口的普通的 bean-post-processors 再其次被注册,对应分类 nonOrderedPostProcessorNames

  4. 最后注册 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
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
/**
* Allows for custom modification of an application context's bean definitions,
* adapting the bean property values of the context's underlying bean factory.
*
* <p>Application contexts can auto-detect BeanFactoryPostProcessor beans in
* their bean definitions and apply them before any other beans get created.
*
* <p>Useful for custom config files targeted at system administrators that
* override bean properties configured in the application context.
*
* <p>See PropertyResourceConfigurer and its concrete implementations
* for out-of-the-box solutions that address such configuration needs.
*
* <p>A BeanFactoryPostProcessor may interact with and modify bean
* definitions, but never bean instances. Doing so may cause premature bean
* instantiation, violating the container and causing unintended side-effects.
* If bean instance interaction is required, consider implementing
* {@link BeanPostProcessor} instead.
*
* @author Juergen Hoeller
* @since 06.07.2003
* @see BeanPostProcessor
* @see PropertyResourceConfigurer
*/
public interface BeanFactoryPostProcessor {

/**
* 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.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

从类的注解上可以看到,它允许对 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
2
john.name: John Doe
jane.name: Jane Doe

创建一个 beans.properties 属性文件,并定义上述两个 property;

beans.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:beans.properties"/>
</bean>

<bean name="john" class="org.shangyang.spring.container.Person">
<property name="name" value="${john.name}"/>
<property name="spouse" ref="jane"/>
</bean>

<bean name="jane" class="org.shangyang.spring.container.Person">
<property name="name" value="${jane.name}"/>
</bean>

</beans>

注入 factory-bean-post-processor PropertyPlaceholderConfigurer到容器中,这样,它会作为回调方法参与 bean 的实例化过程。

工程目录结构

启动容器测试

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testApplicationContext(){

@SuppressWarnings("resource")
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

Person p = context.getBean("john", Person.class);

assertEquals("John Doe", p.getName() );

assertEquals("Jane Doe", p.getSpouse().getName() );

}

_输出_

测试通过,表明,通过 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Instantiate and invoke all registered BeanFactoryPostProcessor beans,
* respecting explicit order if given.
* <p>Must be called before singleton instantiation.
*/
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}

从该方法的注解中我们可以看到,实例化并按照指定的 Order 顺序调用已注册的 bean-factory-post-processors;第 7 行代码是关键,该步骤里面实现了对 bean-factory-post-processors 的实例化和按照顺序注入 Spring 容器的逻辑;从第 11 行代码开始,要注意的是,分别设置了LoadTimeWeaverAwareProcessorContextTypeMatchClassLoader;我们主要关注第 7 行代码 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors,它是如何实现对 bean-factory-post-processors 实例化和注册的;

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors

1
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
public 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 在后期添加的,并不影响核心的逻辑;我们来分析这段代码的逻辑,

  1. 代码第 13 行,从 bean definitions 配置中得到相关的 bean-factory-post-processors name

  2. 代码第 18-34 行,根据 bean-factory-post-processors 所实现的不同的 Order 接口分类
    实现了 PriorityOrdered 接口的放入 priorityOrderedPostProcessors
    实现了 Ordered 接口的放入 orderedPostProcessorNames
    没有实现任何 Order 接口的放入 nonOrderedPostProcessorNames

  3. 实例化 bean-factory-post-processors,代码 26,43,51 行分别对不同分类的 bean-factory-post-processors 进行实例化,该实例化步骤参考 Do Get Bean 流程

  4. 然后,代码 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.java

1
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
*/
@Override
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.java

1
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.java

1
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.
*/
@Override
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,还有很多核心并且好玩的实现类,比如AspectJWeavingEnablerCustomAutowireConfigurerCustomEditorConfigurer等等

总结(我的思考)

BeanFactoryPostProcessor提供了一个无比强大的针对BeanFactory实例的回调接口,从设计初衷上而言,主要是用来修改 bean definitions,进而影响 bean 的实例化过程;上述通过 PropertyPlaceholderConfigurer 给出的测试用例清晰的看到了这一点,我们可以通过外部 properties 文件的内容而影响到最终实例 johnjane 的姓名;我想说的是,这只是小试牛刀,我突然想到一个能够解决让所有从事 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 容器的原因;