3.8.1 使用BeanPostProcessor定制bean

BeanPostProcessor接口定义了回调方法,你可以实现它来提供你自己的(或重载容器的默认)实例化逻辑,依赖解析逻辑等等。如果你想在Spring容器完成实例化,配置和初始化bean之后实现一些自定义逻辑,可以插入一个或多个BeanPostProcessor实现。

你可以配置多个BeanPostProcessor实例,你可以通过设置order属性来控制这些BeanPostProcessor的执行顺序。只有当BeanPostProcessor实现了“Ordered”接口时,才可以设置此属性;如果你写自己的BeanPostProcessor,你应该考虑实现“Ordered”接口。有关更多详细信息,请参阅“BeanPostProcessor”和“有序”接口的javadoc。另见下面关于BeanPostProcessor的程序化注册的注解注册Beanpost处理器)。

[Note]
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,在容器初始化方法(比如InitializingBeanafterProperieSet()方法和其他所有的声明的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的初始化,所以这种尽早的类型检测是至关重要的。

[Note]
虽然BeanPostProcessor注册的推荐方法是通过ApplicationContext自动检测(如上所述),但是也可以使用addBeanPostProcessor方法在ConfigurableBeanFactory上注册它们。 当需要在注册之前评估条件逻辑,或者甚至在层次结构中跨上下文复制bean post processors时,这可能是有用的。 注意,BeanPostProcessors以编程方式添加不遵守Ordered接口。注册的顺序就是执行的次序。 还要注意,“BeanPostProcessor”以编程方式注册,总是在通过自动检测注册之前处理,而不管任何显式排序。
[Note]
实现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属性确实被注入了值。

results matching ""

    No results matching ""