【Spring】扩展点(三):BeanPostProcessor、InstantiationAwareBeanPostProcessor、BeanFactoryPostProcessor
前言:【Spring】扩展点(一):InitialingBean、DisposableBean、BeanPostProcessor、InstantiationAwareBeanPostProcessor1.FactoryBean传统的 Spring 容器加载一个 bean 的整个过程,都是由 Spring 控制的,换句话说,开发者除了设置Bean相关属性之外,是没有太多的自主权的。FactoryB
Spring 扩展点系列:
在前面的文章我们介绍了 InitializingBean、DisposableBean、init-method、destory-method 都是针对的都是某个 bean 控制其初始化的操作(作用域是 bean),那有没有一种办法可以针对每个 bean 的生成前后做一些逻辑操作(作用域是 IOC 容器)?有的,PostProcessor。
1.BeanPostProcessor
public interface BeanPostProcessor {
/**
* 在任何bean初始化回调(例如InitializingBean的{@code afterPropertiesSet}或自定义的init方法)之前,
* 将此BeanPostProcessor应用于给定的新bean实例。
* 该bean已经被属性值填充了。
* 返回的bean实例可能是原始对象的包装。
*
* @param bean --bean的实例
* @param beanName --bean的name
* @return --返回处理过后的bean,可以是最初的Bean或者是包装后的Bean;
* 如果返回null,后续的BeanPostProcessor不会被执行
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* 在任何bean初始化回调(例如InitializingBean的{@code afterPropertiesSet}或自定义的init方法)之后...
* @param bean --bean的实例
* @param beanName --bean的name
* @return --返回处理过后的bean,可以是最初的Bean或者是包装后的Bean;
* 如果返回null,后续的BeanPostProcessor不会被执行
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
PS:这两个方法是有返回值的,不要返回null,否则 getBean() 的时候拿不到对象。
问题一:BeanPostProcessor 作用域是整个容器,所以会对每个 bean 都执行吗?
1)定义两个 Bean,它们都实现了 InitializingBean 和 DisposableBean
public class TestBean03 implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet()..." + "bean03");
}
@Override
public void destroy() throws Exception {
System.out.println("destroy()..." + "bean03");
}
}
public class TestBean04 implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet()..." + "bean04");
}
@Override
public void destroy() throws Exception {
System.out.println("destroy()..." + "bean04");
}
}
2)写一个类实现 BeanPostProcessor 接口,它会对上面的两个 bean 都进行前置与后置处理
public class TestPostProcessorBean implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization()..." + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization()..." + beanName);
return bean;
}
}
3)在 applicationContext.xml 将这三个 bean 配置进去
<bean id="bean03" class="com.xupt.yzh.pkg_04.TestBean03"></bean>
<bean id="bean04" class="com.xupt.yzh.pkg_04.TestBean04"></bean>
<bean id="postprocessorbean" class="com.xupt.yzh.pkg_04.TestPostProcessorBean"></bean>
4)测试代码:
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
context.close(); // close 触发 destroy()
}
}
=> 运行结果如下:
问题二:postProcessBeforeInitialization() 是在 bean 初始化只前还是之后执行的?
我们拿上面的 bean01 试验一下:
<bean id="bean01" class="com.xupt.yzh.pkg_04.TestBean01">
<property name="name" value="张三" />
</bean>
<bean id="postprocessorbean" class="com.xupt.yzh.pkg_04.TestPostProcessorBean"></bean>
最后,对两个重点概念在强调一次:
- BeanPostProcessor 的作用域是整个 IOC 容器,一旦在容器中配置了这个类,那么该容器中所有 bean 在初始化的前后都会调用这个接口对应的方法。
- before 和 after 在方法的注释上面已经说了,是对初始化回调而言,此时已经完成了 bean 的初始化(赋值)。
执行顺序:bean中的初始化方法 -->BeanPostProcessor#before–>InitializingBean–>BeanPostProcessor#after
2.InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor 接口继承 BeanPostProcessor 接口,代表了 Spring 的另一段生命周期:实例化。主要作用在于目标对象的实例化过程中需要处理的事情,包括实例化对象的前后过程以及实例的属性设置
Spring bean 的实例化和初始化是两个连续的阶段:
- 实例化:创建 bean 的过程,即调用 bean 的构造函数,单例的 bean 放入单例池中
- 初始化:赋值的过程,即调用 bean 的 setter,设置Bean的属性
之前的 BeanPostProcessor 是作用于初始化回调前后(此时已经完成了bean的初始化),而 InstantiationAwareBeanPostProcessor 则作用于 bean 实例化前后。
来看看 InstantiationAwareBeanPostProcessor 接口中有哪些方法:
// PS:由于继承了 BeanPostProcessor 接口,所以实现类一共需要实现 2+3=5 个方法
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
/**
* 在实例化目标bean之前应用此BeanPostProcessor。
* 返回的bean对象可能是代替目标bean使用的代理,有效地抑制了目标bean的默认实例化。
* 如果此方法返回了非null对象,则将缩短bean的创建过程。
*
* @param beanClass --要实例化的bean的类
* @param beanName --bean的名称
* @return --bean对象以公开而不是目标bean的默认实例,或{@code null}继续进行默认实例化
*/
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
/**
* 在bean实例化之后,通过构造函数或工厂方法执行操作,但要在发生Spring属性填充(通过显式属性或自动装配)之前进行。
* 这是在给定bean实例上执行自定义字段注入的理想回调,正好在Spring自动装配之前踢进去。
*
* @param bean --创建的bean实例,尚未设置属性
* @param beanName --bean的名称
* @return --是否应该在bean上设置属性;
* 正常的实现应返回{@code true}。
* 返回{@code false}将防止对此bean实例调用任何后续的InstantiationAwareBeanPostProcessor实例
*/
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;
/**
* 在工厂将它们应用于给定bean之前,对给定属性值进行后处理。
* 允许检查是否满足所有依赖关系,例如基于bean属性设置器上的“ Required”注释。
* 还允许替换要应用的属性值,通常是通过基于原始PropertyValues创建新的MutablePropertyValues实例,添加或删除具体值。
*
* @param pvs --工厂将要应用的属性值(永远不会为null})
* @param pds --目标Bean的相关属性描述符(忽略依赖类型-工厂专门处理的-已过滤掉)
* @param bean --创建的bean实例,但是其属性尚未设置
* @param beanName --bean的名称
* @return --实际属性值以应用于给定的bean(可以是传入的PropertyValues实例),或{@code null}
*/
PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException;
}
下面写一个示例来演示一下…
1)写一个普通 bean
public class TestBean05 {
// 创建对象时默认采用空参构造
// 这里是为了演示出来实例化bean的时机
public TestBean05() {
System.out.println("Constructor()...");
}
// 设置一个属性,目的是为了演示初始化bean的时机
private String name;
public void setName(String name) {
this.name = name;
System.out.println("setName()..." + name);
}
}
2)写一个实现类实现 InstantiationAwareBeanPostProcessor 接口(要实现2+3=5个方法):
public class TestInstantiationAwareBeanPostProcessorBean implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor#postProcessBeforeInitialization()...");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor#postProcessAfterInitialization()...");
return bean;
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation()...");
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println("InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()...");
return true;
}
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
System.out.println("InstantiationAwareBeanPostProcessor#postProcessPropertyValues()...");
return pvs;
}
}
3)配置 applicationContext.xml
<bean id="bean05" class="com.xupt.yzh.pkg_04.TestBean05">
<property name="name" value="李四" />
</bean>
<bean id="instantiationAwareBeanPostProcessorBean" class="com.xupt.yzh.pkg_04.TestInstantiationAwareBeanPostProcessorBean"></bean>
=> 启动容器,运行结果如下:
PS:InstantiationAwareBeanPostProcessor 都继承了 BeanPostProcessor,所以也是对于每个 bean 构造时都会执行,这里就不演示了
另外,我们一般不会直接实现 InstantiationAwareBeanPostProcessor 接口,而是会采用继承InstantiationAwareBeanPostProcessorAdapter 这个抽象类的方式来使用。
总结一下,涉及生命周期的扩展接口的执行顺序:
--> InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation()
--> bean 实例化(构造函数)
--> InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()
--> bean 初始化(setter)
--> BeanPostProcessor#postProcessBeforeInstantiation()
--> InitialingBean#afterPropertiesSet()
--> BeanPostProcessor#postProcessAfterInstantiation()
--> bean 使用
--> bean 销毁(容器关闭)
--> DisposableBean#detroy()
3.BeanFactoryPostProcessor
Spring 允许在 bean 创建之前,通过 BeanFactoryPostProcessor 读取 bean 的元属性,并根据自己的需求对元属性进行改变(比如将 bean 的 scope 从singleton改变为prototype)
PS:这里说的 bean 元属性是作用域、是否懒加载等,不是成员变量的值!
/**
* 允许自定义修改应用程序上下文的Bean定义,
* 调整上下文基础bean工厂的bean属性值。
*
* <p>应用程序上下文可以在其bean定义中自动检测BeanFactoryPostProcessor bean,
* 并在创建任何其他bean之前先创建BeanFactoryPostProcessor。
*
* <p>对于针对用户的自定义配置文件很有用
* 覆盖应用程序上下文中配置的bean属性。
*
* <p> BeanFactoryPostProcessor可以与bean definitions打交道,
* 但是千万不要进行bean实例化。这样做可能会导致bean被提前实例化,
* 会破坏容器造成预估不到的副作用。
* 如果你需要hack到bean实例化过程,请考虑使用BeanPostProcessor。
*/
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;
}
PS:BeanFactoryPostProcessor 接口和 BeanPostProcessor 无直接关系
问题一:BeanFactoryPostProcessor 作用域也是容器,所以它的方法也会对于每个bean都执行一次吗?
1)我们先来写一个实现类实现 BeanFactoryPostProcessor 接口
public class TestFactoryPostProcessorBean implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("postProcessBeanFactory()...");
}
}
2)在 applicationContext.xml 将上篇文章的 bean03、bean04 和 TestFactoryPostProcessorBean 配置进去
<bean id="bean01" class="com.xupt.yzh.pkg_04.TestBean03"></bean>
<bean id="bean03" class="com.xupt.yzh.pkg_04.TestBean04"></bean>
<bean id="factorypostprocessorbean" class="com.xupt.yzh.pkg_04.TestFactoryPostProcessorBean"></bean>
=> 运行结果如下:
问题二:InitialingBean、BeanPostProcessor、InstantiationAwareBeanPostProcessor、BeanFactoryPostProcessor 执行顺序?
上面已经说了前三个扩展点的执行顺序,所以我们这里只需要 一个 FactoryPostProcessorBean 的 bean,一个能演示实例化/初始时机bean(上篇的bean05),和 InstantiationAwareBeanPostProcessor 的 bean 就可以定位 FactoryPostProcessorBean 的执行时机:
<bean id="factorypostprocessorbean" class="com.xupt.yzh.pkg_04.TestFactoryPostProcessorBean"></bean>
<bean id="bean05" class="com.xupt.yzh.pkg_04.TestBean05">
<property name="name" value="李四" />
</bean>
<bean id="instantiationAwareBeanPostProcessorBean" class="com.xupt.yzh.pkg_04.TestInstantiationAwareBeanPostProcessorBean"></bean>
=> 执行结果如下:
其实 BeanFactoryPostProcessor 为什么最先执行也很好想,因为只有先将 BeanDefinition 理顺才可能进入到 bean 实例化阶段。
问题三:BeanFactoryPostProcessor 如何实现 bean 属性配置?
可以看到 postProcessBeanFactory() 方法的入参有一个 ConfigurableListableBeanFactory,它的功能非常丰富,最基本的,它携带了每个Bean的基本信息,所以它可以用它来获取 BeanDefinition,然后修改 bean 的属性。
1)我们来修改一下上面的 postProcessBeanFactory 方法:
public class TestFactoryPostProcessorBean implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("postProcessBeanFactory()...");
BeanDefinition bean03 = beanFactory.getBeanDefinition("bean03");
System.out.println("bean03原来的scope:" + bean03.getScope());
bean03.setScope("singleton");
System.out.println("bean03现在的scope:" + bean03.getScope());
}
}
2)修改一下 applicationContext.xml 中 bean03 的 scope 信息(指定为多例 prototype)
<bean id="bean03" class="com.xupt.yzh.pkg_04.TestBean03" scope="prototype"></bean>
<bean id="factorypostprocessorbean" class="com.xupt.yzh.pkg_04.TestFactoryPostProcessorBean"></bean>
=> 运行结果如下:
ConfigurableListableBeanFactory 还有很多方法,比如添加 BeanPostProcessor,可以自己去查看。
问题四:BeanDefinitionRegistryPostProcessor 跟 BeanFactoryPostProcessor 什么关系?
BeanDefinitionRegistryPostProcessor 接口继承了 BeanFactoryPostProcessor ,也是只有一个方法:
// 实现 BeanDefinitionRegistryPostProcessor 接口后要同时重写 postProcessBeanFactory() 和 postProcessBeanDefinitionRegistry()
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
/**
* 标准初始化后,修改应用程序上下文的内部所有 BeanDefinitions。
* 所有常规 BeanDefinition 都将被加载,但此时尚未实例化任何 bean。
* 它允许在下一个处理阶段开始之前添加更多的 BeanDefinition。
*
* @param registry --应用程序上下文使用的 BeanDefinitionRegistry
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
BeanDefinitionRegistry 提供了比 ConfigurableListableBeanFactory 更多对 BeanDefinition 操作的方法,比如 registerBeanDefinition() 方法去注册新的 BeanDefinition。
在 Spring 整合 mybatis 就用到了 BeanDefinitionRegistryPostProcessor 这个扩展点,因为 Spring 无法处理 Mapper 接口,所以 mybatis-spring.jar 中就通过实现该接口的 registerBeanDefinition() 实现了自定义 BeanDefinition 然后将它自行注册到 IOC 容器中。
PS:关于这块内容的详细分析可以参考我的这篇博客…
更多推荐
所有评论(0)