在前面两篇关于容器扩展的文章,我们已经完成了对 BeanFactoryPostProcessor 和 FactoryBean 的分析,对于 BeanFactoryPostProcessor 而言,它能让我们对容器中扫描出来的 BeanDefinition 做出修改以达到扩展的目的,而对于 FactoryBean 而言,它提供了一种特殊创建 bean 的手段,能让我们将一个对象直接放入容器中,成为 Spring 所管理的一个 bean。而这篇文章将要说的 BeanPostProcessor 不同于上面两个接口,它主要干预的是 Spring 中 bean 的整个生命周期(实例化---属性填充---初始化---销毁),关于 bean 的生命周期将在下篇文章中介绍。
按照管理,先看官网对 BeanPostProcessor 的介绍
从这段文字中,我们能获取到如下信息:
- BeanPostProcessor 接口定义了两个回调方法,通过实现这两个方法我们可以提供自己的实例化以及依赖注入逻辑。而且,如果我们想要 Spring 容器完成实例化,配置以及初始化一个 bean 后进行一些定制的逻辑,我们可以插入一个甚至多个 BeanPostProcessor 的实现。
- 我们可以配置多个 BeanPostProcessor,并且只有我们配置的 BeanFactoryPostProcessor 同时实现了 Ordered 接口的话,还可以控制这些 BeanPostProcessor 执行的顺序。
我们通过一个例子来看看 BeanPostProcessor 的作用
应用举例
demo:
// 自己实现了一个 BeanPostProcessor
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("indexService")) {
System.out.println(bean);
System.out.println("bean config invoke postProcessBeforeInitialization");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("indexService")) {
System.out.println(bean);
System.out.println("bean config invoke postProcessAfterInitialization");
}
return bean;
}
}
@Component
public class IndexService {
@Autowired
LuBanService luBanService;
@Override
public String toString() {
return "IndexService{" +
"luBanService=" + luBanService +
'}';
}
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
}
}
运行上面的程序:
IndexService{luBanService=com.dmz.official.extension.entity.LuBanService@5e025e70}
bean config invoke postProcessBeforeInitialization
IndexService{luBanService=com.dmz.official.extension.entity.LuBanService@5e025e70}
bean config invoke postProcessAfterInitialization
从上面的执行结果可以得出一个结论,BeanPostProcessor 接口中的两个方法的执行时机在属性注入之后。因为从打印的结果可以发现,IndexService 中的 luBanService 属性以及被注入了。
接口继承关系
由于 BeanPostProcessor 这个接口 Spring 本身内置的实现类有很多,所以这里暂且不分析其实现类,就从接口的定义上来分析它的作用,其接口的 UML 类图如下:
- BeanPostProcessor,这个接口是我们 bean 的后置处理器的顶级接口,其中主要包含了两个方法
// 在 bean 初始化前调用
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// 在 bean 初始化后调用
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
- InstantiationAwareBeanPostProcessor,继承了 BeanPostProcessor 接口,并在此基础上扩展了 4 个方法,其中方法 postProcessPropertyValues 以及在 5.1 版本中废弃:
大部分情况下我们在扩展时都不会用到 postProcessProperties 和 postProcessPropertyValues,如果在某些场景下不得不用到这两个方法,那么请注意,在实现 postProcessProperties 必须返回 null,否则 postProcessPropertyValues 的逻辑不会只想。
- SmartInstantiationAwareBeanPostProcessor,继续扩展了上面的接口,并多提供了三个方法:
这个接口的三个方法一般是在 Spring 内部使用,可以关注这个接口上的一段 Java doc
上面这段文字很明确的指出了这个接口的设计是为了一些特殊的目的,主要是在 Spring 框架内部使用,通常来说我们提供的后置处理器只有实现 BeanPostProcessor 或者 InstantiationAwareBeanPostProcessorAdapter 即可。正常情况下,我们在扩展时不需要考虑着几个方法。
- DestructionAwareBeanPostProcessor,这个接口直接继承了 BeanPostProcessor,同时多提供了两个方法,主要用于 bean 在进行销毁时进行回调
- MergedBeanDefinitionPostProcessor,这个接口也直接继承了 BeanPostProcessor,但是多提供了两个方法。
源码分析
我们带着两个问题去阅读源码:
- 容器这么多 BeanPostProcessor,它们是按什么顺序执行的?
- BeanP 接口中这么多方法,它们的执行时机是什么时候?
接下来解决这两个问题
执行顺序
在 Spring 内部,当去执行一个 BeanPostProcessor 一般都是采用下面这种形式的代码:
for (BeanPostProcessor bp : getBeanPostProcessors()) {
// 判断属于某一类后置处理器
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
// 执行逻辑
}
}
}
getBeanPostProcessors()获取到的 BeanPostProcessor 其实就是一个 list 集合,所以我们要分析 BeanPostProcessor 的执行顺序,其实就是分析这个 list 集合中的数据是通过什么顺序添加进来的,先来看看之前说的 Spring 的执行流程图:
我们这次要分析的代码就是其中的 3-6 步骤,代码如下:
疑惑代码解读
下面对上述代码进行一波分析:
- 获取容器中已经注册的 bean 的名称,根据 BeanDefinition 中获取 BeanName
这里主要是根据已经注册在容器中的 BeanDefinition,这些 BeanDefinition 即包括程序员自己注册到容器中的,也包括 Spring 自己注册到容器中。注意这些后置处理器目前没有被创建,只是以 BeanDefinition 的形式存在于容器,所以如果此时调用 getBeanPostProcessors(),是拿不到这些后置处理器的,至于容器是什么时候注册了后置处理器的 BeanDefinition,大家可以先自行阅读 1-1
步骤的源码,我在后续文章中会分析,当前就暂时先跳过了
- 通过addBeanPostProcessor方法添加的BeanPostProcessor以及注册到容器中的BeanPostProcessor的总数量
这里主要是获取容器中已经存在的BeanPostProcessor的数量再加上已经被扫描出来的BeanDefinition的后置处理器的属性(这些后置处理器还没有被创建出来),最后加1.这里主要两个问题:
- 容器已经存在的BeanPostProcessor是从哪里来的?
分为两个来源,第一,容器启动时,自身调用了addBeanPostProcessor添加了后置处理器;第二,程序员手动调用了addBeanPostProcessor方法添加了后置处理器。第二种情况很少见,代码如下面这种形式:
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(Appconfig.class);
ac.getBeanFactory().addBeanPostProcessor(new MyBeanPostProcessor());
ac.refresh();
}
容器又是在什么时候添加的后置处理器的呢?代码很深,不去看了,以后再说。
- 为什么最后还需要加1?
这个和我们将要分析的第三行代码相关
- 3.添加一个BeanPostProcessorChecker,主要用于日志记录
我们看下BeanPostProcessorChecker这个类的源码:
这段代码主要关注两个方法:
- isInfrastructureBean,这个方法主要检查当前处理的bean是否是一个Spring自身需要创建的bean,而不是程序员所创建的bean(通过@Component,@Configuration等注解或者XML配置等)。
- postProcessAfterInitialization,我们可以看到这个方法内部只是做了一个判断,只有当前创建的bean不是一个后置处理器并且不是一个Spring自身需要创建的基础的bean,最后还有一个判断this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount,这个其实就是说在创建bean时容器中的后置处理器还没有完全创建完。这个判断也能解释我们上面遗留的一个问题,之所以加1,是为了方便判断,否则还需要进行等号判断
- 上面代码标注的4-7就不解释了,只需要注意的就是registerBeanPostProcessors方法中调用了一个addBeanPostProcessor(BeanPostProcessor beanPostProcessor);,我们看下这个方法的执行逻辑:
- 注意下第8点代码,对于没有实现任何排序接口的后置处理器,Spring是不会进行排序操作的,即使你添加了@Order注解也没用。这里只针对Spring Framework。
- 第10点代码又添加了一个后置处理器,添加这个后置处理器主要是为了可以检测到所有的事件监听器,我们看下它的代码:
这个后置处理器注意针对事件监听器(Spring中的事件监听器机制以后在说,这里就把它当做Spring中一个特殊的bean)。上面的代码3-5步可能会让人迷惑,实际上我在之前话的执行流程图中的3-10步Spring就已经注册过一次监听器了,在3-10步骤中,其实Spring已经通过String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);这段代码拿到了所有的名字,那么我们思考一个问题,为什么Spring不直接根据这些名字取过滤创建的bean,而要通过一个特点的后置处理器去进行处理呢?比如可以通过下面这种逻辑:
if(listenerBeanNames.contains(beanName)){
this.applicationContext.addApplicationListener(bean);
}
这是因为有一种特殊的bean,它会由Spring来创建,自身却不在Spring容器中,这种特殊的bean就是嵌套bean。注意这里说的是嵌套bean,不是内不类,是由下面的XML配置的bean:
<bean class="com.dmz.official.service.IndexService" id="indexService">
<property name="luBanService">
<bean class="com.dmz.official.service.LuBanService"/>
</property>
<property name="dmzService" ref="dmzService"/>
</bean>
在上面的例子中,LuBanService就是一个嵌套的bean。
假设我们上面的LuBanService是一个事件监听器,那么在getBeanNamesForType这个方法执行时,是无法获取到这个bean的名称的。所以Spring专门提供了上述的那个后置处理器,用于处理这种嵌套bean的情况,但是所提供的嵌套bean必须是单例的。
在分析执行时机时,我们先要知道Spring在创建一个bean时要经历哪些阶段,这里其实涉及到bean的生命周期了,在下篇文章我会专门分析Spring的生命周期,这里主要说明后置处理器的执行时机,先进行一些大致的介绍。
总结
这篇文章主要说了Spring中最后一个扩展点BeanPostProcessor,这里只是对BeanPostProcessor中的方法及执行顺序大致的了解,但是目前为止还不知道每个方法具体的执行时机是什么时候,这个问题放在下篇文章中,结合Spring官网的生命周期回调方法的相关内容一起分析。到此为止可以简单总结如下:
- BeanPostProcessor,主要用于干预bean的创建过程。
- BeanFactoryPostProcessor,主要用于针对容器中 的BeanDefinition
- FactoryBean,主要用于将一个对象直接放入到Spring容器中,同时可以封装复杂对象的创建逻辑