3.8.1 使用BeanPostProcessor定制bean
BeanPostProcessor
接口定义了回调方法
,你可以实现它来提供你自己的(或重载容器的默认)实例化逻辑,依赖解析逻辑等等。如果你想在Spring容器完成实例化,配置和初始化bean之后实现一些自定义逻辑,可以插入一个或多个BeanPostProcessor实现。
你可以配置多个BeanPostProcessor
实例,你可以通过设置order
属性来控制这些BeanPostProcessor的执行顺序。只有当BeanPostProcessor实现了“Ordered”接口时,才可以设置此属性;如果你写自己的BeanPostProcessor,你应该考虑实现“Ordered”接口。有关更多详细信息,请参阅“BeanPostProcessor”和“有序”接口的javadoc。另见下面关于BeanPostProcessor的程序化注册的注解注册Beanpost处理器)。
BeanPostProcessor 操作bean(或对象)实例 ; 也就是说,Spring IoC容器实例化一个bean实例,然后BeanPostProcessor 做他们的工作.BeanPostProcessor 的作用域是每个容器。 这仅在使用容器层次结构时才会用到这个。 如果你在一个容器中定义一个BeanPostProcessor ,它将只对该容器中的bean进行后处理。 换句话说,在一个容器中定义的bean不会被另一个容器中定义的BeanPostProcessor进行后处理,即使两个容器都是同一层次结构的一部分。要改变实际的bean定义(即blueprint 定义bean,译者注:应该是指各种配置元数据,比如xml、注解等),你需要使用一个BeanFactoryPostProcessor ,如第3.8.2节“使用BeanFactoryPostProcessor定制配置元数据”。 |
org.springframework.beans.factory.config.BeanPostProcessor
接口恰好包含两个回调方法。当这样的类在容器内注册为post-processor
,对于由容器创建的每个bean实例,容器创建所有bean,在容器初始化方法(比如InitializingBean
的afterProperieSet()
方法和其他所有的声明的init
方法)和所有bean 初始化回调之前,运行post-processor
回调。post-processor
可以对bean实例采取任何操作,包括完全忽略回调。 bean post-processor
通常检查回调接口或者可以用代理包装bean。一些Spring AOP基础结构类被实现为bean post-processor
,以便提供代理包装逻辑。
一个ApplicationContext
自动检测
在配置元数据中定义的实现BeanPostProcessor
接口的任何bean。 ApplicationContext
将这些bean注册为后处理器,以便稍后在创建bean时调用它们。 Bean后处理器可以像任何其他bean一样部署在容器中。
注意,当在配置类上使用@Bean
工厂方法声明BeanPostProcessor
时,工厂方法的返回类型应该是实现类本身或至少是org.springframework.beans.factory.config.BeanPostProcessor
接口,清楚地表明该bean的后处理器性质。否则,ApplicationContext
将不能在完全创建它之前通过类型自动检测它。因为BeanPostProcessor
需要尽早的实例化,以便应用于上下文中其他bean的初始化,所以这种尽早的类型检测是至关重要的。
虽然BeanPostProcessor 注册的推荐方法是通过ApplicationContext自动检测(如上所述),但是也可以使用addBeanPostProcessor 方法在ConfigurableBeanFactory 上注册它们。 当需要在注册之前评估条件逻辑,或者甚至在层次结构中跨上下文复制bean post processors 时,这可能是有用的。 注意,BeanPostProcessor s以编程方式添加不遵守Ordered接口 。注册的顺序就是执行的次序。 还要注意,“BeanPostProcessor”以编程方式注册,总是在通过自动检测注册之前处理,而不管任何显式排序。 |
实现BeanPostProcessor接口的类是special 的,并且容器的处理方式不同。所有BeanPostProcessor 和他们直接引用的bean 在启动时被实例化,作为ApplicationContext的特殊启动阶段的一部分。接下来,所有的BeanPostProcessors 都会按照次序注册到容器中,在其他bean使用BeanPostProcessors 处理时也会使用此顺序。因为AOP的auto-proxying自动代理是BeanPostProcessor 的默认实现,它既不引用BeanPostProcessors 也不引用其他bean,不会发生auto-proxying自动代理,因此不会有切面织入。对于任何这样的bean,你应该看到一个信息日志消息:“Bean foo不能由所有的BeanPostProcessor接口处理(例如:不符合自动代理) ”注意,如果你有bean连接到你的BeanPostProcessor 使用autowiring或@Resource (可能回退到自动装配),Spring可能会在搜索类型匹配依赖关系候选时访问意外的bean,因此使它们不适合自动代理或他们是其他种类的bean post-processing。例如,如果你有一个使用@Resource 注解的依赖,其中field/ setter名称不直接对应于bean的声明名称,并且没有使用name属性,那么Spring将访问其他bean以按类型匹配它们。 |
以下示例显示如何在ApplicationContext中写入,注册和使用BeanPostProcessor。
Example: Hello World, BeanPostProcessor-style
第一个例子说明了基本用法。 该示例显示了一个自定义的BeanPostProcessor实现,它调用每个bean的toString()
方法,因为它是由容器创建的,并将结果字符串打印到系统控制台。
下面找到自定义的“BeanPostProcessor”实现类定义:
package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
// simply return the instantiated bean as-is
public Object postProcessBeforeInitialization(Object bean,
String beanName) throws BeansException {
return bean; // we could potentially return any object reference here...
}
public Object postProcessAfterInitialization(Object bean,
String beanName) throws BeansException {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
return 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"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang
http://www.springframework.org/schema/lang/spring-lang.xsd">
<lang:groovy id="messenger"
script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
<lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
</lang:groovy>
<!--
when the above bean (messenger) is instantiated, this custom
BeanPostProcessor implementation will output the fact to the system console
-->
<bean class="scripting.InstantiationTracingBeanPostProcessor"/>
</beans>
注意InstantiationTracingBeanPostProcessor
是如何定义的。 它甚至没有名称,并且因为它是一个bean,它可以像任何其他bean一样依赖注入。 (前面的配置也定义了一个由Groovy脚本支持的bean。Spring动态语言支持在标题为第31章,动态语言支持
.)
以下简单的Java应用程序执行上述代码和配置:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;
public final class Boot {
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
Messenger messenger = (Messenger) ctx.getBean("messenger");
System.out.println(messenger);
}
}
上面应用程序的输出类似于以下内容:
Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961
示例:RequiredAnnotationBeanPostProcessor
使用回调接口或注解结合自定义的“BeanPostProcessor”实现是扩展Spring IoC容器的常见手段。。例如Spring的RequiredAnnotationBeanPostProcessor
,是个BeanPostProcessor
实现类,spring内置,作用是确保Spring bean定义上的带注解的JavaBean属性确实被注入了值。