参考资料:
《Spring的BeanFactoryPostProcessor和BeanPostProcessor》
《Spring IoC源码学习:invokeBeanFactoryPostProcessors 详解》
《Spring IoC源码学习:registerBeanPostProcessors 详解》
写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。
在学习IOC的过程中,发现对于BeanFactoryPostProcessor(实例工厂后置处理器)和BeanPostProcessor(实例后置处理器)之间的关系存在一些疑问,这里结合查到的几份资料整理了一下。
一、简单概括
这两个接口,都是Spring初始化bean时对外暴露的扩展点。两个接口名称看起来很相似,但作用及使用场景却不同。
1、BeanFactoryPostProcessor
BeanFactoryPostProcessor是 Spring 初始化 BeanFactory 时对外暴露的扩展点,Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它。该接口只有一个postProcessBeanFactory方法,在bean初始化前调用。
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
2、 BeanPostProcessor
BeanPostProcessor接口是 Spring 初始化 bean 时对外暴露的扩展点,Spring IoC 容器允许 BeanPostProcessor 在容器初始化 bean 的前后,添加自己的逻辑处理。该接口有两个方法,postProcessBeforeInitialization和postProcessAfterInitialization,都是在bean初始化后调用,区别在于
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
3、异同
简单总结一下,就是BeanFactoryPostProcessor和BeanPostProcessor都是Spring初始化时提供的扩展点,即我们可以通过实现这两个接口实现对bean的修改。区别在于,前者是对BeanFactory(即bean的定义,bean definitions)的扩展,在bean初始化前调用,后者是对bean (即实例对象)的扩展,在bean初始化后调用。
二、使用示范
下面,使用xml配置的方式调用自定义接口配置
1、myTestBean
这里加了2个初始化相关参数,作为补充
(1)afterPropertiesSet
接口InitializingBean定义的方法,在初始化过程自动中被调用
(2)initMethod
xml配置方式中,可在bean属性中设置init-method="initMethod"指定,作用同上
public class MyTestBean implements InitializingBean
{
private String value1;
private String value2;
public MyTestBean() {
System.out.println("===调用构造方法===");
}
public String getValue1() {
return value1;
}
public void setValue1(String value1) {
System.out.println("===调用setValue1方法===");
this.value1 = value1;
}
public String getValue2() {
return value2;
}
public void setValue2(String value2) {
System.out.println("===调用setValue2方法===");
this.value2 = value2;
}
public void afterPropertiesSet() throws Exception {
System.out.println("===调用afterPropertiesSet方法===");
}
public void initMethod() {
System.out.println("===调用initMethod方法===");
}
public String toString() {
StringBuilder builder = new StringBuilder();
System.out.println("value1="+this.value1);
System.out.println("value2="+this.value2);
return builder.toString();
}
}
2、MyBeanFactoryPostProcessor
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition bd = beanFactory.getBeanDefinition("myTestBean");
if(bd!=null){
System.out.println("===调用MyBeanFactoryPostProcessor的postProcessBeanFactory===");
System.out.println("===获取myTestBean的作用域:"+bd.getScope());
bd.setScope("singleton");
System.out.println("===修改myTestBean的作用域:"+bd.getScope());
MutablePropertyValues pv = bd.getPropertyValues();
if (pv.contains("value2")) {
System.out.println("===在BeanFactoryPostProcessor中修改value2的值为:参数值n===");
pv.addPropertyValue("value2", "参数值n");
}
}
}
}
3、MyBeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if("myTestBean".equals(beanName)){
System.out.println("===调用postProcessBeforeInitialization方法,"+beanName);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if("myTestBean".equals(beanName)){
System.out.println("===调用postProcessAfterInitialization方法,"+beanName);
}
return bean;
}
}
4、springContext.xml
<bean id="myTestBean" class="com.test.MyTestBean" init-method="initMethod"
scope="prototype">
<property name="value1" value="参数值1" />
<property name="value2" value="参数值2" />
</bean>
<bean id="myBeanPostProcessor" class="com.test.MyBeanPostProcessor" />
<bean id="myBeanFactoryPostProcessor" class="com.test.MyBeanFactoryPostProcessor" />
5、Test类
public class myTest
{
public static void main(String[] args) {
@SuppressWarnings("resource")
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("springContext.xml");
MyTestBean bean = (MyTestBean) context.getBean("myTestBean");
System.out.println("===============下面输出结果============");
System.out.println("value1=" + bean.getValue1());
System.out.println("value2=" + bean.getValue2());
}
}
6、结果展示
===调用MyBeanFactoryPostProcessor的postProcessBeanFactory===
===获取myTestBean的作用域:prototype
===修改myTestBean的作用域:singleton
===在BeanFactoryPostProcessor中修改value2的值为:参数值n===
===调用构造方法===
===调用setValue1方法===
===调用setValue2方法===
===调用postProcessBeforeInitialization方法,myTestBean
===调用afterPropertiesSet方法===
===调用initMethod方法===
===调用postProcessAfterInitialization方法,myTestBean
===============下面输出结果============
value1=参数值1
value2=参数值n
我们可以看到调用过程如下
三、补充
1、关于执行顺序
我们知道BeanFactoryPostProcessor————>初始化————>BeanPostProcessor
在上面的例子里,在BeanFactoryPostProcessor中对bena的value2的值做了修改,这就属于对bean的修改,但不推荐,原因后面会讲。同时也对scope做了修改,将多例变为了单例,这就属于bean Factory的修改,是BeanFactoryPostProcessor真正应该起作用的地方。
2、不推荐BeanFactoryPostProcessor中对bean的修改
主要有以下2方面原因
(1)处在调用链的最顶端
由于BeanFactoryPostProcessor在调用链的最顶端,这导致后面的修改都有可能将BeanFactoryPostProcessor中对bean的修改覆盖掉,但BeanFactory只能在这里修改,所以不用担心BeanFactory的改动被覆盖。
(2)无法使用自动注入
这里我们需要简单看一下bean实例化的核心方法AbstractApplicationContext中的refresh调用过程
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
这里会先调用invokeBeanFactoryPostProcessors,实例化和调用所有 BeanFactoryPostProcessor,然后调用registerBeanPostProcessors,注册所有的BeanPostProcessor,将所有实现了 BeanPostProcessor 接口的类加载到 BeanFactory 中。
然而,@AutoWired和@Resource都是依赖于BeanPostProcessor的实现类。BeanFactoryPostProcessor执行时@AutoWired和@Resource还未注册,,无法使用自动注入功能来修改bean。因此,只能通过配置xml的方式来实现,在大型项目中,这必然是无法接受的,所以不推荐在BeanFactoryPostProcessor中修改bean