定义:它也是 Spring
对外提供的接口,用来给用户扩展自定义的功能。执行的时机在 bean
实例化阶段前后
本篇思路:
BeanPostProcessor
定义- 如何使用
- 代码实现分析
- 介绍剩余的扩展功能
前言
与 BeanFactoryPostProcessor
不同的是,BeanFactoryPostProcessor
的注册和执行都在同一个方法内,而 BeanPostProcessor
分开两个方法,分为注册和调用两个步骤。
常规的 BeanFactory
中是没有实现后处理器的自动注册,所以在调用的时候没有进行手动注册是无法使用的,但在 ApplicationContext
中添加了自动注册功能(在这个 registerBeanPostProcessors
方法中),最后在 bean
实例化时执行 BeanPostProcessor
对应的方法。
本次主要介绍 BeanPostProcessor
,同时也会将剩下的 context
扩展功能一起学习~
BeanPostProcessor
经过上一篇文章的学习,应该对 bean
的后处理理解起来更顺利,下面直奔主题,来看下它是如何使用和结合源码分析
如何使用
新建一个 bean 后处理器
这个后处理器需要引用 InstantiationAwareBeanPostProcessor
接口(实际继承自 BeanPostProcessor
),然后重载以下两个方法:
public class CarBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 这里没有区分 bean 类型,只是用来测试打印的顺序和时间 System.out.println("Bean name : " + beanName + ", before, time : " + System.currentTimeMillis()); return null; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("Bean name : " + beanName + ", after time : " + System.currentTimeMillis()); return null; } }
在配置文件中注册 bean-post-processor.xml
在配置文件配置我们写的自定义后处理器和两个普通 bean
,用来测试打印时间和顺序
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- beanPostProcessor --> <bean id="carPostProcessor" class="context.bean.CarBeanPostProcessor"/> <!--用以下两个 bean 进行测试打印时间和顺序--> <bean id="car" class="base.factory.bean.Car"> <property name="price" value="10000"/> <property name="brand" value="奔驰"/> </bean> <bean id="book" class="domain.ComplexBook"/> </beans>
启动代码和打印结果
public class CarBeanPostProcessorBootstrap { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("factory.bean/bean-post-processor.xml"); Car car = (Car) context.getBean("car"); ComplexBook book = (ComplexBook) context.getBean("book"); System.out.println(car); System.out.println(book); } }
输出:
Bean name : car, before Initialization, time : 1560772863996 Bean name : car, after Initialization, time : 1560772863996 Bean name : book, before Initialization, time : 1560772863999 Bean name : book, after Initialization, time : 1560772863999 Car{maxSpeed=0, brand='奔驰', price=10000.0} domain.ComplexBook@77be656f
从输出接口看出,打印顺序是先框架内部,再到应用层,框架内部中,在顺序实例化每个 bean
时,前面也提到执行时机:先执行 postProcessBeforeInitialization
方法,然后实例化 bean
后,执行 postProcessAfterInitialization
。
所以我们重载的两个接口按照前后顺序打印出来了~
注册 BeanPostProcessor
上面介绍了使用例子,应该不难理解,接着来看下源码注册的方法:
org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors
实际委托给了 PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
public static void registerBeanPostProcessors( ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) { // 注释 7.2 从注册表中取出 class 类型为 BeanPostProcessor 的 bean 名称列表 String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false); int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length; beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount)); // 将带有 权限顺序、顺序和其余的 beanPostProcessor 分开 List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); // 类型是 MergedBeanDefinitionPostProcessor List<BeanPostProcessor> internalPostProcessors = new ArrayList<>(); List<String> orderedPostProcessorNames = new ArrayList<>(); List<String> nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { // 分类,添加到对应数组中 ... } // 首先,注册实现了 PriorityOrdered 接口的 bean 后处理器 sortPostProcessors(priorityOrderedPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors); // 下一步,注册实现了 Ordered 接口的 bean 后处理器 List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); for (String ppName : orderedPostProcessorNames) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); orderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } sortPostProcessors(orderedPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, orderedPostProcessors); // 现在,注册常规 bean 后处理器,其实就是不带顺序 List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); for (String ppName : nonOrderedPostProcessorNames) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); nonOrderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors); // 最后,重新注册 MergedBeanDefinitionPostProcessor 类型的后处理器 // 看起来是重复注册了,但是每次注册调用的底层方法都会先移除已存在的 beanPostProcessor,然后再加进去,最后还是保存唯一 sortPostProcessors(internalPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, internalPostProcessors); // 添加 ApplicationContext 探测器 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext)); }
跟之前的 BeanFactoryPostProcessor
处理是不是很相似,也是进行分类,将带有权重顺序、顺序和普通 BeanPostProcessor
添加到对应的列表后,然后排序,统一注册到 beanPostProcessors
列表末尾。
将 BeanPostProcessor
与之前的 BeanFactoryPostProcessor
进行对比后发现,少了硬编码注册的代码,只处理了配置文件方式的注册 bean
。通过书中阐释,对少了硬编码的处理有些理解:
对于 BeanFactoryPostProcessor 的处理,在一个方法内实现了注册和实现,所以需要载入配置中的定义,并进行激活;而对于 BeanPostProcessor 并不需要马上调用,硬编码的方式实现的功能是将后处理器提取并调用,对于 BeanPostProcessor,注册阶段不需要调用,所以没有考虑处理硬编码,在这里只需要将配置文件的 BeanPostProcessor 提取出来并注册进入 beanFactory 就可以了。
而且我在测试过程,想在应用代码中进行硬编码注册,发现由于 ClassPathXmlApplicationContext
最后一个方法是实例化非延迟加载的 bean
,在上下文创建好时,BeanPostProcessor
就已经执行完成了,于是硬编码注册的后处理器无法执行,只能通过设定延迟加载或者在配置文件配置中进行注册,或者其它 BeanFactory 能支持硬编码。
剩下顺序 Order
类型的后处理器注册 BeanFactoryPostProcessor
类似就不重复多讲解了,这段代码的逻辑挺清晰的~
小结
结束两个扩展功能,BeanFactoryPostProcessor
和 BeanPostProcessor
的学习使用后,还有其它的扩展功能没学习到,在一开始基础机构篇就提到剩下的方法:
这这些扩展功能中,个人感觉事件传播器、监听器和发送广播事件这三个会用得比较多,所以下面的内容会花比较大篇幅讲这三个扩展。