BeanPostProcessor 接口定义了回调方法,您可以实现这些方法来提供自己的(或覆盖容器的默认)实例化逻辑、依赖项解析逻辑等等。如果希望在 Spring 容器完成 bean 的实例化、配置和初始化后实现一些自定义逻辑,可以插入一个或多个自定义 BeanPostProcessor 实现。
您可以配置多个 BeanPostProcessor 实例,并且可以通过设置 order 属性来控制这些 BeanPostProcessor 实例的运行顺序。只有当 BeanPostProcessor 实现了 Ordered 接口时,才能设置此属性。如果编写自己的 BeanPostProcessor,也应该考虑实现 Ordered
接口。有关更多详细信息,请参阅 BeanPostProcessor和 Ordered 接口的 javadoc。另请参见 BeanPostProcessor 实例的编程注册说明(本章后续内容)。
- BeanPostProcessor 实例对 bean(或对象)实例进行操作。也就是说,Spring IoC 容器实例化一个 bean 实例,然后 BeanPostProcessor 实例执行它们的工作。
- BeanPostProcessor 实例的作用域为每个容器。这仅在使用容器层次结构时才相关。如果在一个容器中定义 BeanPostProcessor,它将只处理该容器中的 bean。换句话说,一个容器中定义的 bean 不会被另一个容器中定义的 BeanPostProcessor 后处理,即使两个容器都是同一层次结构的一部分。
- 要更改实际的 bean 定义(即定义 bean 的蓝图),需要使用 BeanFactoryPostProcessor,如使用BeanFactoryPostProcessor 自定义配置元数据中所述。
org.springframework.beans.factory.config.BeanPostProcessor
接口正好由两个回调方法组成。当这样的类被注册为容器的后处理器时,对于容器创建的每个 bean 实例,后处理器在容器初始化方法(如 InitializingBean.afterPropertiesSet()
或任何已声明的 init
方法)被调用之前和任何 bean 初始化回调之后都会从容器获得一个回调。后处理程序可以对 Bean 实例采取任何行动,包括完全忽略回调。后处理器通常会检查回调接口,或者用代理来包装 bean 。一些 Spring AOP 基础设施类被实现为 Bean 后处理器,以提供代理包裹逻辑。
ApplicationContext 会自动检测配置元数据中定义的实现 BeanPostProcessor 接口的任何 Bean。ApplicationContext 将这些 bean 注册为后处理器,以便以后在创建 bean 时可以调用它们。Bean 后处理器可以像其他 Bean 一样被部署在容器中。
请注意,通过在配置类上使用 @Bean
工厂方法来声明 BeanPostProcessor
时,工厂方法的返回类型应该是实现类本身,或者至少是 org.springframework.beans.factory.config.BeanPostProcessor
接口,明确表示该 Bean 的后处理性质。否则,ApplicationContext 无法在完全创建它之前按类型自动检测它。由于BeanPostProcessor 需要尽早被实例化,以便应用于上下文中其他 Bean 的初始化,所以这种早期的类型检测是至关重要的。
以编程方式注册 BeanPostProcessor 实例
虽然推荐的 BeanPostProcessor 注册方法是通过 ApplicationContext 自动检测(如前所述),但你可以通过使用 ConfigurableBeanFactory.addBeanPostProcessor
方法以编程方式注册它们。当你需要在注册前评估条件逻辑或甚至在一个层次结构中跨上下文复制 Bean Post 处理器时,这可能很有用。然而,请注意,以编程方式添加的 BeanPostProcessor 实例并不尊重 Ordered 接口。这里,是注册的顺序决定了执行的顺序。还要注意的是,以编程方式注册的 BeanPostProcessor 实例总是在通过自动检测注册的实例之前被处理,而不考虑任何明确的顺序。
BeanPostProcessor 实例和 AOP 自动代理
实现了 BeanPostProcessor 接口的类很特别,容器对它们的处理方式也不同。所有 BeanPostProcessor 实例和它们直接引用的 Bean 在启动时被实例化,作为 ApplicationContext 特殊启动阶段的一部分。接下来,所有的 BeanPostProcessor 实例被分类注册,并应用于容器中的所有其他 Bean。因为 AOP 自动代理是作为 BeanPostProcessor 本身实现的,所以 BeanPostProcessor 实例和它们直接引用的 Bean 都不符合自动代理的条件,因此,没有方面交织在一起。
对于任何这样的 Bean,你应该看到一个信息性的日志消息: Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying).
Bean someBean 不符合由所有 BeanPostProcessor 接口处理的条件(例如:不符合自动代理的条件)。
如果你通过使用 autowiring 或 @Resource(可能会退回到 autowiring)将 Bean 连接到BeanPostProcessor,Spring 在搜索类型匹配的依赖候选者时可能会访问意外的 Bean,因此,使它们没有资格进行自动代理或其他类型的 Bean 后处理。例如,如果你有一个用 @Resource 注解的依赖,其中字段或设置器的名称不直接对应于 Bean 的声明名称,并且没有使用名称属性,Spring 会访问其他 Bean 以通过类型匹配它们。
下面的示例演示如何在 ApplicationContext 中编写、注册和使用 BeanPostProcessor 实例。
例子:Hello World BeanPostProcessor
这第一个例子说明了基本用法。这个例子显示了一个自定义的 BeanPostProcessor 实现,它在容器创建每个 Bean 时调用 toString()
方法,并将结果字符串打印到系统控制台。
下面的列表显示了自定义 BeanPostProcessor 的实现类定义。
package cn.mrcode.study.springdocsread;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* @author mrcode
*/
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
/**
* <pre>
* 在任何 bean 初始化回调(如 InitializingBean 的 afterPropertiesSet 或自定义 init 方法)之前将此 BeanPostProcessor
* 应用于给定的新 bean 实例。 bean 将已填充属性值。返回的 bean 实例可能是原始的包装器。 默认实现按原样返回给定的 bean
*
* 简单说:在 ioc 容器填充完属性后,调用初始化方法前,回调该方法
* </pre>
*
* @param bean bean 实例对象
* @param beanName 该 bean 的名称
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 这里简单的返回给定的 bean ,也可以返回任意对象引用
return bean;
}
/**
* <pre>
* 在任何 bean 初始化回调(如 InitializingBean 的 afterPropertiesSet 或自定义 init 方法)之后,将此 BeanPostProcessor
* 应用于给定的新 bean 实例。 bean 将已填充属性值。返回的 bean 实例可能是原始的包装器。 在使用 FactoryBean 的情况下,将为 FactoryBean 实例和由
* FactoryBean 创建的对象(从 Spring 2.0 开始)调用此回调。后处理器可以通过相应的 bean instanceof FactoryBean 检查来决定是否应用到
* FactoryBean 或创建的对象或两者。 与所有其他 BeanPostProcessor 回调相比,
* 此回调也将在由I nstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
* 方法触发的短路后调用。 默认实现按原样返回给定的 bean 。
*
* 简单说:在 ioc 容器填充完属性后,调用初始化方法后,回调该方法
* </pre>
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean '" + beanName + "' 已创建 : " + bean.toString());
return bean;
}
}
xml 声明
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"
>
<!-- 声明 InstantiationTracingBeanPostProcessor,这里采用的是 ioc 容器自动发现的方式注册 BeanPostProcessor
也就是实现了 BeanPostProcessor 接口,ioc 容器就会自动发现并在合适的时机使用他们
-->
<bean class="cn.mrcode.study.springdocsread.InstantiationTracingBeanPostProcessor"></bean>
<!-- 实例化一个普通的对象 -->
<bean id="hello" class="cn.mrcode.study.springdocsread.web.Hello"></bean>
</beans>
容器启动测试
public class TestDemo {
public static void main(String[] args) {
final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
System.out.println();
}
}
当容器启动后,会发现控制台打印了一条信息
Bean 'hello' 已创建 : cn.mrcode.study.springdocsread.web.Hello@74e52ef6
注意 InstantiationTracingBeanPostProcessor 是如何声明的,它甚至没有声明 bean 名称,因为它是一个 bean 所以可以像其他 bean 一样被依赖注入
例子:AutowiredAnnotationBeanPostProcessor
将回调接口或注解与自定义 BeanPostProcessor 实现结合起来使用是扩展 Spring IoC 容器的一种常见手段。一个例子是 Spring 的 AutowiredAnnotationBeanPostProcessor 它是一个 BeanPostProcessor 实现,它与 Spring 的发布一起,自动连接注释字段、设置方法和任意的配置方法。
简单说,@Autowired 和 @Value
的功能就是 AutowiredAnnotationBeanPostProcessor 实现的,所以如果对 BeanPostProcessor 有兴趣的话,可以去看看这个类的源码