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>

在这里插入图片描述
最后,对两个重点概念在强调一次:

  1. BeanPostProcessor 的作用域是整个 IOC 容器,一旦在容器中配置了这个类,那么该容器中所有 bean 在初始化的前后都会调用这个接口对应的方法。
  2. before 和 after 在方法的注释上面已经说了,是对初始化回调而言,此时已经完成了 bean 的初始化(赋值)。

执行顺序:bean中的初始化方法 -->BeanPostProcessor#before–>InitializingBean–>BeanPostProcessor#after

2.InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor 接口继承 BeanPostProcessor 接口,代表了 Spring 的另一段生命周期:实例化。主要作用在于目标对象的实例化过程中需要处理的事情,包括实例化对象的前后过程以及实例的属性设置

Spring bean 的实例化和初始化是两个连续的阶段:

  1. 实例化:创建 bean 的过程,即调用 bean 的构造函数,单例的 bean 放入单例池中
  2. 初始化:赋值的过程,即调用 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:关于这块内容的详细分析可以参考我的这篇博客

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐