Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.8 容器扩展点 6.8.1 使用BeanPostProcessor定制Bean

6.8 容器扩展点

通常,应用程序开发人员不需要实现ApplicationContext子类。反而,可以通过专门的集成接口的实现以插件形式来扩展Spring IoC容器。接下来的几节将介绍这些集成接口。

6.8.1 使用BeanPostProcessor定制Bean

BeanPostProcessor接口定义了回调方法,通过实现它来提供您自己的(或覆盖容器的默认)实例化逻辑,依赖关系解析逻辑等。如果要在Spring容器完成实例化,配置和初始化bean之后实现某些自定义逻辑,则能够以插件方式集成一个或多个BeanPostProcessor的实现。

您可以配置多个BeanPostProcessor实例,并且可以通过设置order属性来控制这些BeanPostProcessors的执行顺序。仅当BeanPostProcessor实现Ordered接口时,才能设置此属性; 如果你编写自己的BeanPostProcessor,你也应该考虑实现Ordered接口。有关更多详细信息,请参阅BeanPostProcessor和Ordered接口的javadoc。另请参阅以下有关BeanPostProcessors的程序化注册的说明。

BeanPostProcessors在bean(或对象)实例上运行; 也就是说,Spring IoC容器实例化一个bean实例,然后BeanPostProcessors就执行它们的工作。
BeanPostProcessors的作用域是每个容器。这仅在您使用容器层级时才有意义。如果在一个容器中定义BeanPostProcessor,它将只对该容器中的bean进行后置处理。 换句话说,在一个容器中定义的bean不会被另一个容器中定义的BeanPostProcessor进行后置处理,即使两个容器都是同一层级的一部分。
要变更实际的bean定义(即定义bean的蓝图),您需要使用BeanFactoryPostProcessor,如第6.8.2节“使用BeanFactoryPostProcessor定制配置元数据”中所述。

org.springframework.beans.factory.config.BeanPostProcessor接口由两个回调方法组成。 当这样的类被注册为容器的后置处理器时,对于容器创建的每个bean实例,在调用容器初始化方法(例如InitializingBean的afterPropertiesSet()以及声明的init方法)之前和在任何bean初始化回调之后,后处理器都可以从容器中获得回调。后处理器可以对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注册的推荐方法是通过ApplicationContext自动检测(如上所述),但也可以使用addBeanPostProcessor方法以编程方式对ConfigurableBeanFactory注册它们。 当需要在注册之前评估条件逻辑,或者甚至在跨上下文的层次结构中复制bean后处理器时,这是非常有用的。但请注意,以编程方式添加的BeanPostProcessors不遵循Ordered接口,在这里是说注册的顺序就是执行的顺序。另请注意,不管是否显式地声明了顺序,以编程方式注册的BeanPostProcessors始终在通过自动检测注册的BeanPostProcessors之前处理。

实现BeanPostProcessor接口的类是特殊的,容器会对它们进行差异处理。直接引用的所有BeanPostProcessors和bean都会在启动时实例化,作为ApplicationContext的专有启动阶段的一部分。接下来,所有BeanPostProcessors都以排序方式注册,并应用于容器中的今后所有其他的bean。由于AOP自动代理是作为BeanPostProcessor本身实现的,所以BeanPostProcessors和它们直接引用的bean都不适合自动代理,因此将它们应用于切面。
对于任何此类bean,您应该看到一条信息性日志消息:“Bean foo不适合所有BeanPostProcessor接口处理(例如:不符合自动代理条件)”。
请注意,如果使用自动装配或@Resource将bean连接到BeanPostProcessor(可能会回退到自动装配),Spring可能会在搜索类型匹配依赖候选项时访问意外的bean,因而使它们不符合自动代理或其他bean后处理类型。例如,如果您有一个使用@Resource注解的依赖项,其中field / setter名称不直接对应于bean的声明名称而且没有使用name属性,那么Spring就会通过类型匹配来访问其他bean。

以下示例显示如何在ApplicationContext中编写,注册和使用BeanPostProcessors。

示例:Hello World,BeanPostProcessor样式

第一个例子说明了基本用法。该示例显示了一个自定义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动态语言支持在名为第34章,动态语言支持章节中有详细介绍。)
以下是用于执行上述代码和配置的简单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——一个随Spring发行版一起提供的BeanPostProcessor实现,它确保用(任意)注解标记的bean上的JavaBean属性是真实的(配置为)依赖注入值。

原文地址:https://www.cnblogs.com/springmorning/p/10361660.html