第7章 IoC容器 III (Container) -- Spring4.3.8参考文档中文版

7.8容器扩展要点

@sunRainAmazing

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

7.8.1使用BeanPostProcessor自定义bean

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

您可以配置多个BeanPostProcessor实例,您可以BeanPostProcessor通过设置order属性来控制这些实例的执行顺序。只有在BeanPostProcessor实现Ordered接口时才可以设置此属性; 如果你自己写,BeanPostProcessor你应该考虑实现Ordered 接口。有关更多详细信息,请参阅BeanPostProcessor和 Ordered接口的javadocs 。另请参阅下面的注释 的纲领性注册BeanPostProcessor小号。

【注】BeanPostProcessor在bean(或对象)实例上操作 ; 也就是说,Spring IoC容器实例化一个bean实例,然后 BeanPostProcessor执行它们的工作。

BeanPostProcessor每个容器的范围。这只有在使用容器层次结构时才有用。如果您BeanPostProcessor在一个容器中定义一个容器,则只会对该容器中的bean进行后处理。换句话说,在一个容器中定义的bean不会BeanPostProcessor在另一个容器中定义的后处理,即使这两个容器都是同一层次结构的一部分。

要改变实际的bean定义(即蓝图定义bean),而不是你需要使用BeanFactoryPostProcessor在描述 第7.8.2节”自定义配置元数据与BeanFactoryPostProcessor的”。

该org.springframework.beans.factory.config.BeanPostProcessor接口由两个回调方法组成。当这样的类被注册为具有容器的后处理器时,对于由容器创建的每个bean实例,后处理器在容器初始化方法之前从容器获得回调(诸如InitializingBean的afterPropertiesSet()和任何声明init方法)被称为以及之后的任何bean初始化回调。后处理器可以对bean实例执行任何操作,包括完全忽略回调。bean后处理器通常检查回调接口,或者可以使用代理来包装bean。一些Spring AOP基础设施类被实现为bean后处理器,以提供代理包装逻辑。

ApplicationContext 自动检测,其中实施所述配置元数据中定义的任何beanBeanPostProcessor接口。将 ApplicationContext这些bean注册为后处理器,以便稍后在bean创建时可以调用它们。Bean后处理器可以像任何其他bean一样部署在容器中。

请注意,当在配置类上声明BeanPostProcessor使用@Bean工厂方法时,工厂方法的返回类型应该是实现类本身或至少是org.springframework.beans.factory.config.BeanPostProcessor 接口,清楚地指示该bean的后处理器性质。否则,ApplicationContext在完全创建之前, 将无法通过类型自动检测它。由于BeanPostProcessor需要早期实例化以适用于上下文中其他bean的初始化,因此此早期类型检测至关重要。

【注】而对于建议的方法BeanPostProcessor注册是通过 ApplicationContext自动检测(如上文所述),但也可以注册它们编程对一个ConfigurableBeanFactory使用addBeanPostProcessor方法。当需要在注册之前评估条件逻辑,或者甚至在层次结构中的背景上复制bean后处理器时,这可能是有用的。但是请注意,BeanPostProcessor以编程方式添加不遵守该Ordered界面。这是注册执行顺序的顺序。还要注意,BeanPostProcessor以编程方式注册的s始终在通过自动检测注册的那些之前进行处理,无论任何明确的排序。

【注】实现BeanPostProcessor接口的类是特殊的,容器对待的方式不同。他们直接引用的所有BeanPostProcessors 和bean都将在启动时实例化,作为特殊启动阶段的一部分 ApplicationContext。接下来,所有BeanPostProcessors都以排序方式注册,并应用于容器中的所有其他bean。因为AOP自动代理是作为一个BeanPostProcessor自身实现的,所以BeanPostProcessor它们直接引用的bean和它们都不符合自动代理的资格,因此没有方面的编译。

对于任何这样的bean,您应该看到一个信息日志消息:” Bean foo不符合所有BeanPostProcessor接口处理(例如:不符合自动代理资格) “的资格。

请注意,如果您将bean连接到BeanPostProcessor使用自动装配或 @Resource(可能会退回到自动连线),则Spring可能会在搜索匹配相关候选项时访问意外的bean,因此使其不符合自动代理或其他类型的bean帖-处理。例如,如果您有一个依赖注释,@Resource其中field /setter名称与bean的声明名称不直接对应,并且不使用name属性,那么Spring将访问其他bean以便按类型进行匹配。

以下示例显示如何在其中写入,注册和使用BeanPostProcessors ApplicationContext。
示例:Hello World,BeanPostProcessor-style
第一个例子说明了基本用法。该示例显示了一个自定义 BeanPostProcessor实现,它调用toString()由容器创建的每个bean 的方法,并将生成的字符串打印到系统控制台。
查找下面的自定义BeanPostProcessor实现类定义:

package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
    //简单地将实例化的bean返回为
    public Object postProcessBeforeInitialization(Object bean,
            String beanName)throws BeansException {
         return bean; //我们可以在这里返回任何对象引用
    }
    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" 
    xmlnsxsi = "http://www.w3.org /2001 /XMLSchema-instance" 
    xmlnslang = "http://www.springframework.org/schema/lang" 
    xsischemaLocation = "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 = "classpathorg /springframework /scripting /groovy /Messenger.groovy" > 
    <lang:property name = "message" value = "Fiona Apple is so so dreamy" /> 
    </lang:groovy>
    <!-- 当上面的bean(messenger)被实例化,这个习惯
         BeanPostProcessor实现将事实输出到系统控制台
    --> 
    <bean class = "scripting.InstantiationTracingBeanPostProcessor" /></bean>
</beans>

注意如何InstantiationTracingBeanPostProcessor简单地定义。它甚至没有一个名字,因为它是一个bean,它可以像任何其他bean一样被依赖注入。(上述配置还定义了一个由Groovy脚本支持的bean,Spring动态语言支持在第35章” 动态语言支持 “一章中详细介绍 。
以下简单的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’创建:

org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961

示例:RequiredAnnotationBeanPostProcessor
将回调接口或注释与自定义BeanPostProcessor实现结合使用 是扩展Spring IoC容器的常用手段。Spring的一个例子是Spring 发行版附带的RequiredAnnotationBeanPostProcessor一个 BeanPostProcessor实现,它确保在标记有(任意)注释的bean上实现(配置为)依赖注入一个值的JavaBean属性。

7.8.2使用BeanFactoryPostProcessor自定义配置元数据

我们将看到的下一个扩展点是 org.springframework.beans.factory.config.BeanFactoryPostProcessor。
这个接口的语义类似于这个接口的BeanPostProcessor一个主要区别:BeanFactoryPostProcessor在bean配置元数据上运行 ; 也就是说,Spring IoC容器允许BeanFactoryPostProcessor读取配置元数据,并在容器实例化除s 之外的任何bean之前潜在地更改它BeanFactoryPostProcessor。
您可以配置多个BeanFactoryPostProcessors,您可以BeanFactoryPostProcessor通过设置order属性来控制这些执行顺序。但是,如果BeanFactoryPostProcessor实现Ordered接口,则只能设置此属性。如果你自己编写BeanFactoryPostProcessor,你也应该考虑实现Ordered接口。请参阅BeanFactoryPostProcessor和Ordered接口的javadocs 了解更多详细信息。

【注】如果要更改实际的bean 实例(即从配置元数据创建的对象),则需要使用BeanPostProcessor (在第7.8.1节”使用BeanPostProcessor自定义bean”中描述)。虽然在技术上可以BeanFactoryPostProcessor使用(例如,使用 BeanFactory.getBean())中的bean实例,但这样做会导致早期的bean实例化,违反了标准容器生命周期。这可能会导致负面的副作用,如旁路bean后处理。
此外,每个容器的BeanFactoryPostProcessor范围。这只有在使用容器层次结构时才有用。如果在一个容器中定义一个容器,那么它只会应用于该容器中的bean定义。一个容器中的Bean定义不会被另一个容器中的s 进行后处理,即使这两个容器都是同一层次结构的一部分。BeanFactoryPostProcessorBeanFactoryPostProcessor

Bean Factory后处理程序在内部声明时自动执行 ApplicationContext,以便对定义容器的配置元数据应用更改。Spring包含了一些预定义的bean factory后处理器,如PropertyOverrideConfigurer和 PropertyPlaceholderConfigurer。BeanFactoryPostProcessor也可以使用自定义,例如,注册自定义属性编辑器。
一个ApplicationContext自动检测部署在它实现了任何beanBeanFactoryPostProcessor接口。它在适当的时候使用这些bean作为bean类工厂后处理器。您可以像任何其他bean一样部署这些后处理器bean。
【注】与BeanPostProcessors一样,您通常不想配置 BeanFactoryPostProcessors进行延迟初始化。如果没有其他bean引用 Bean(Factory)PostProcessor,则后处理器将不会被实例化。因此,将其标记为延迟初始化将被忽略,并且 Bean(Factory)PostProcessor即使将default-lazy-init属性设置为元素true的声明,也将热切地实例化 。
示例:类名替换PropertyPlaceholderConfigurer
您可以PropertyPlaceholderConfigurer使用标准Java Properties格式在单独文件中的bean定义中外部化属性值。这样做使得部署应用程序的人可以自定义环境特定的属性(如数据库URL和密码),而不会复杂或风险修改容器的主要XML定义文件或文件。
考虑以下基于XML的配置元数据片段,其中DataSource 定义了占位符值。该示例显示从外部Properties文件配置的属性。在运行时,将PropertyPlaceholderConfigurer应用于将替换DataSource的某些属性的元数据。要替换的值被指定为遵循Ant /log4j /JSP EL样式的形式的占位符${property-name}。

<bean class = "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" > 
    <property name = "locations" value = "classpath:com /foo /jdbc.properties" /> </bean>
<bean id = "dataSource" destroy-method = "close" class = "org.apache.commons.dbcp.BasicDataSource" > 
    <property name = "driverClassName" value = "$ {jdbc.driverClassName}" /> 
    <property name = "url" value = "$ {jdbc.url}" /> 
    <property name = "username" value = "$ {jdbc.username}" /> 
    <property name = "password" value = "$ {jdbc.password }" /> </bean>

实际值来自标准Java Properties格式的另一个文件:

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

因此,字符串${jdbc.username}在运行时被替换为值”sa”,同样适用于与属性文件中的键匹配的其他占位符值。在PropertyPlaceholderConfigurer为大多数属性和bean定义的属性占位符检查。此外,可以定制占位符前缀和后缀。
通过context在Spring 2.5中引入的命名空间,可以使用专用配置元素配置属性占位符。可以在location属性中以逗号分隔的列表提供一个或多个位置。

<contextproperty-placeholder  location = "classpath:com /foo /jdbc.properties" />

在PropertyPlaceholderConfigurer不仅将查找在属性Properties 指定的文件。默认情况下,System如果在指定的属性文件中找不到属性,它还会检查Java 属性。您可以通过将systemPropertiesModeconfigurer 的属性设置为以下三个受支持的整数值之一来自定义此行为:

1  never(0):不要检查系统属性
2  fallback(1):如果在指定的属性文件中不可解析,请检查系统属性。这是默认值。
3  override(2):在尝试指定的属性文件之前先检查系统属性。这允许系统属性覆盖任何其他属性源。

请查询PropertyPlaceholderConfigurerjavadocs了解更多信息。

【注】您可以使用PropertyPlaceholderConfigurer替换类名称,这在运行时必须选择特定的实现类时有时很有用。例如:

<bean  class = "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" > 
    <property  name = "locations" > 
        <value> classpath:com /foo /strategy.properties </value> 
    </property> 
    <property  name = "properties" > 
        <value> custom.strategy.class = com.foo.DefaultStrategy </value> 
    </property> </bean><bean  id = "serviceStrategy"  class = "$ {custom.strategy.class}" />

如果类不能在运行时被解析为一个有效的类,bean的分辨率,当它即将被创造,这是在失败preInstantiateSingletons() 的阶段ApplicationContext对非延迟实例化的bean。
示例:PropertyOverrideConfigurer
在PropertyOverrideConfigurer另一个bean工厂后置处理器,类似 PropertyPlaceholderConfigurer,但不同的是后者,原来的定义可以有缺省值或者根本没有值的bean属性。如果重写 Properties文件没有某个bean属性的条目,则使用默认上下文定义。
请注意,bean定义不知道被覆盖,所以从XML定义文件中不能立即显示覆盖配置程序正在被使用。在PropertyOverrideConfigurer同一个bean属性定义不同值的多个实例的情况下,由于覆盖机制,最后一个实例获胜。
属性文件配置行采用以下格式:

beanName.property =value

例如:

dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb

此示例文件可以与包含称为dataSource的bean的容器定义一起使用,该bean 具有驱动程序和URL属性。
还支持复合属性名称,只要除了最终属性被覆盖的路径的每个组件都已经是非空(可能由构造函数初始化)。在这个例子中…

foo.fred.bob.sammy = 123

1.bean 的sammy属性的bob属性的fred属性foo设置为标量值123。
【注】指定的覆盖值始终为字面值 ; 它们不会被转换为bean引用。当XML bean定义中的原始值指定一个bean引用时,此约定也适用。

使用contextSpring 2.5引入的命名空间,可以使用专用配置元素配置属性覆盖:

<contextproperty-override  location = "classpath:override.properties" />

7.8.3使用FactoryBean自定义实例化逻辑

org.springframework.beans.factory.FactoryBean为自己的工厂的对象实现接口 。
该FactoryBean接口是Spring IoC容器的实例化逻辑的可插入点。如果您有复杂的初始化代码,在Java中更好地表达,而不是(可能)冗长的XML,则可以创建自己的FactoryBean,在该类中编写复杂的初始化,然后将自定义FactoryBean插入到容器中。
该FactoryBean界面提供三种方法:

  Object getObject():返回此工厂创建的对象的实例。该实例可能是共享的,这取决于这个工厂是否返回单例或原型。
  boolean isSingleton():返回true如果FactoryBean返回单身, false否则。
  Class getObjectType():返回getObject()方法返回的对象类型,或者null类型未提前知道。

该FactoryBean概念和接口被一些Spring框架内的地方使用; FactoryBean与Spring本身一起运行的界面的50多个实现。
当您需要向容器询问实际的FactoryBean实例本身而不是其生成的bean &时,在调用该getBean()方法时,使用&符号()来表示bean的id ApplicationContext。所以对于一个给定FactoryBean 的id myBean,调用getBean(“myBean”)容器返回的产品FactoryBean; 而调用getBean(“&myBean”)返回 FactoryBean实例本身。

7.9基于注释的容器配置

注释比XML更适合配置Spring吗?
引入基于注释的配置提出了这种方法是否比XML更好的问题。简短的答案是否取决于。长远的答案是,每种方法都有其优缺点,通常由开发人员决定哪种策略更适合他们。由于它们被定义的方式,注释在它们的声明中提供了大量的上下文,从而导致更短和更简洁的配置。然而,XML在不接触源代码或重新编译组件的情况下,不但可以组合组件。一些开发人员喜欢将接线靠近源,而其他开发者认为注释类不再是POJO,此外,配置变得分散化,难以控制。

无论选择,Spring都可以容纳两种风格,甚至混合在一起。值得指出的是,通过其JavaConfig选项,Spring允许以非侵入式的方式使用注释,而不会触及目标组件源代码,而在工具方面,Spring Tool Suite支持所有配置样式 。

基于注释的配置提供了XML设置的替代方法,该配置依赖于字节码元数据来连接组件,而不是角括号声明。开发人员通过使用相关类,方法或字段声明中的注释来将配置移动到组件类本身,而不是使用XML来描述一个bean布线。正如在”Example:The RequiredAnnotationBeanPostProcessor”一节中所提到的,使用BeanPostProcessor结合注释是扩展Spring IoC容器的常用手段。例如,Spring 2.0引入了使用@Required注释强制执行所需属性的可能性。Spring 2.5使得可以遵循相同的一般方法来驱动Spring的依赖注入。本质上,@Autowired注释提供了与第7.4.5节”自动布线协作者”中所述相同的功能,但具有更细粒度的控制和更广泛的适用性。Spring 2.5还增加了对JSR-250注释的支持,如 @PostConstruct和@PreDestroy。Spring3.0添加JSR-330(Java依赖注入)包含在所述包javax.inject注释如支持@Inject 和@Named。有关这些注释的详细信息,请参见 相关章节。

【注】在 XML注入之前执行注解注入,因此后一种配置将覆盖前者通过这两种方法连接的属性。

一如以往,您可以将它们注册为单独的bean定义,但也可以通过在基于XML的Spring配置中包含以下标记来隐式注册(注意包含context命名空间):

<?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
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd" >
    <context:annotation-config/></bean>

(该隐式注册的后处理器包括 AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor,以及前述 RequiredAnnotationBeanPostProcessor)。

【注】<context:annotation-config/>只在同一个应用程序上下文中定义bean的注释。这意味着,如果你把 <context:annotation-config/>一个WebApplicationContext一个DispatcherServlet,它只是检查@Autowired在控制器bean类,而不是你的服务。有关详细信息,请参见 第22.2节”DispatcherServlet”。
7.9.1 @Required
该@Required注释适用于bean属性setter方法,如下面的例子:

public class SimpleMovieLister {
    private  MovieFinder movieFinder ;
    @Required
    public void setMovieFinder(MovieFinder movieFinder){
         this .movieFinder = movieFinder;
    }
    //...
}

此注释仅仅表示受影响的bean属性必须在配置时通过bean定义中的显式属性值或通过自动布线来填充。如果未填写受影响的bean属性,容器将抛出异常; 这允许急切和明确的失败,以后避免NullPointerExceptions等。仍然建议您将断言放入bean类本身,例如,将其放入init方法中。这样做即使在使用容器外的类时也会执行这些必需的引用和值。

7.9.2 @Autowired

【注】以下示例中@Inject可以使用JSR 330的注释来代替Spring的@Autowired注释。请参阅这里了解更多详情。
您可以将@Autowired注释应用于构造函数:

public class MovieRecommender {
    private final CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao){
         this .customerPreferenceDao = customerPreferenceDao;
    }
    //...
}

【注】从Spring Framework 4.3开始,@Autowired如果目标bean只定义了一个构造函数,则不再需要构造函数。如果有几个构造函数可用,至少必须注释一个构造函数来指导容器必须使用哪个构造函数。
如预期,您还可以将@Autowired注释应用于”传统”设置器方法:

public class SimpleMovieLister {
    private MovieFinder movieFinder;
    @Autowired
    public void setMovieFinder(MovieFinder movieFinder){
         this .movieFinder = movieFinder;
    }
    //...
}

您还可以将注释应用于具有任意名称和/或多个参数的方法:

public class MovieRecommender {
    private MovieCatalog movieCatalog;
    private CustomerPreferenceDao customerPreferenceDao;
    @Autowired
     public void prepare(MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao){
        this .movieCatalog = movieCatalog;
        this .customerPreferenceDao = customerPreferenceDao;
    }
    //...
}

您也可以申请@Autowired到字段,甚至与构造函数混合使用:

public class MovieRecommender {
    private final CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    private MovieCatalog movieCatalog;
    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao){
         this .customerPreferenceDao = customerPreferenceDao;
    }
    //...
}

通过将注释添加到期望该类型的数组的字段或方法,也可以提供特定类型的所有 bean ApplicationContext:

public class MovieRecommender {
    @Autowired
    private MovieCatalog [] movieCatalogs;
    //...
}

类型集合也是一样:

public class MovieRecommender {
    private Set <MovieCatalog> movieCatalogs;
    @Autowired
    public void setMovieCatalogs(Set <MovieCatalog> movieCatalogs){
         this .movieCatalogs = movieCatalogs;
    }
    //...
}

如果您希望将数组或列表中的项目按特定顺序排序,您的bean可以实现该org.springframework.core.Ordered接口或使用@Order或标准@Priority注释。
只要预期的键类型,即使输入的map也可以自动连线String。Map值将包含预期类型的​​所有bean,键将包含相应的bean名称:

public class MovieRecommender {
    private Map <String,MovieCatalog> movieCatalogs;
    @Autowired
    public void setMovieCatalogs(Map <String,MovieCatalog> movieCatalogs){
         this .movieCatalogs = movieCatalogs;
    }
    //...
}

默认情况下,自动布线在零候选bean可用时失败; 默认行为是将注释的方法,构造函数和字段视为指示所需的依赖关系。可以如下所示更改此行为。

public  class SimpleMovieLister {
    private MovieFinder movieFinder ;
    @Autowired(required = false)
    public void setMovieFinder(MovieFinder movieFinder){
         this .movieFinder = movieFinder;
    }
    //...
}

【注】每个类只有一个注释构造函数可以被标记为必需的,但是可以注释多个非必需的构造函数。在这种情况下,每个都被考虑在候选中,而Spring使用最可靠的构造函数,其依赖性可以被满足,也就是具有最大参数数的构造函数。
@Autowired’s required attribute is recommended over the `@Required注解。将所需的属性表示该属性不需要自动装配的目的,如果它不能自动装配的属性被忽略。@Required另一方面,它更强大,它强制通过容器支持的任何方式设置的属性。如果没有注入值,则会引发相应的异常。

您还可以使用@Autowired对于那些众所周知的解析依赖接口:BeanFactory,ApplicationContext,Environment,ResourceLoader, ApplicationEventPublisher,和MessageSource。这些接口及其扩展接口(例如ConfigurableApplicationContext或ResourcePatternResolver)自动解决,无需特殊设置。

public class MovieRecommender {
    @Autowired
    private ApplicationContext context;
    public MovieRecommender(){
    }
    //...
}

【注】@Autowired,@Inject,@Resource,和@Value注释由Spring处理 BeanPostProcessor实现这反过来又意味着你不能在您自己的应用这些注释BeanPostProcessor或BeanFactoryPostProcessor类型(如果有的话)。这些类型必须通过XML或使用Spring @Bean方法显式地”连线” 。

7.9.3使用@Primary对基于注释的自动布线进行微调

因为根据类型的自动装配可能导致多个候选者,所以通常需要对选择过程有更多的控制。实现这一点的一个方法是使用Spring的 @Primary注释。@Primary表示当多个bean是自动连接到单值依赖关系的候选者时,应该给予特定的bean优先权。如果候选人中只存在一个”主要”bean,那么它将是自动连线值。
我们假设我们有以下配置定义firstMovieCatalog为 主要 配置MovieCatalog。

@Configuration 
public class MovieConfiguration {
    @Bean 
    @Primary
    public MovieCatalog firstMovieCatalog(){...}
    @Bean
    public MovieCatalog secondMovieCatalog(){...}
    //...
}

通过这样的配置,以下MovieRecommender将自动连接 firstMovieCatalog。

public class MovieRecommender {
    @Autowired
    private MovieCatalog movieCatalog;
    //...
}

相应的bean定义如下所示。

<?xml version ="1.0"encoding ="UTF-8"> 
<beans xmlns = "http://www.springframework.org/schema/beans" 
    xmlnsxsi = "http://www.w3.org /2001 /XMLSchema-instance" 
    xmlnscontext = "http://www.springframework.org/schema/context" 
    xsischemaLocation = "http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd" >
    <context:annotation-config/>
    <bean class = "example.SimpleMovieCatalog" primary ="true" >
         < - 注入此bean所需的任何依赖关系 - > 
    </bean>
    <bean class = "example.SimpleMovieCatalog" > 
    < - 注入此bean所需的任何依赖关系 - > 
    </bean>
    <bean id = "movieRecommender" class = "example.MovieRecommender" /></bean>

7.9.4使用限定符微调基于注释的自动布线

@Primary当一个主要候选人可以确定时,是使用具有多个实例的类型的自动装配的有效方式。当需要对选择过程的更多控制时,@Qualifier可以使用Spring的注释。您可以将限定符值与特定参数相关联,缩小类型匹配集,以便为每个参数选择特定的bean。在最简单的情况下,这可以是一个简单的描述性值:

public class MovieRecommender {
    @Autowired 
    @Qualifier("main")
     private MovieCatalog movieCatalog;
    //...
}

的@Qualifier注释也可以在单独的构造器参数或方法参数指定:

public class MovieRecommender {
    private MovieCatalog movieCatalog;
    private CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    public void prepare( @Qualifier("main") MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao){
        this .movieCatalog = movieCatalog;
         this .customerPreferenceDao = customerPreferenceDao;
    }
    //...
}

相应的bean定义如下所示。具有限定符值”main”的bean使用具有相同值限定的构造函数参数进行连接。

<?xml version ="1.0"encoding ="UTF-8"> 
<beans xmlns = "http://www.springframework.org/schema/beans" 
    xmlnsxsi = "http://www.w3.org /2001 /XMLSchema-instance" 
    xmlnscontext = "http://www.springframework.org/schema/context" 
    xsischemaLocation = "http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd" >
    <context:annotation-config/>
    <bean class = "example.SimpleMovieCatalog" > 
    <qualifier value ="main"/>
    < - 注入此bean所需的任何依赖关系 - > 
    </bean>
    <bean class = "example.SimpleMovieCatalog" > 
    <qualifier value ="action"/>
    < - 注入此bean所需的任何依赖关系 - > 
    </bean>
    <bean id = "movieRecommender" class = "example.MovieRecommender" /></bean>

对于回退匹配,bean名称被认为是默认限定符值。因此,您可以使用id”main”来定义bean,而不是嵌套限定符元素,导致相同的匹配结果。但是,虽然可以使用这个约定来引用特定的bean,但是@Autowired它基本上是关于类型驱动的注入与可选语义限定符。这意味着限定符值,即使使用bean名称回退,在类型匹配集合中总是具有缩小的语义; 他们没有在语义上表达对唯一bean ID的引用。良好的限定符值是”主”或”EMEA”或”持久”,表示独立于bean的特定组件的特征id,可以在上述示例中的匿名bean定义的情况下自动生成。
限定符也适用于类型化的集合,如上所述,例如 Set<MovieCatalog>。在这种情况下,根据声明的限定符的所有匹配的bean将作为集合进行注入。这意味着限定词不一定是唯一的;它们只是构成过滤标准。例如,您可以MovieCatalog使用相同的限定符值”action” 定义多个bean,所有这些bean都将注入到Set<MovieCatalog>注释中@Qualifier(“action”)。

【注】如果您打算以名称表示注释驱动的注入,那么@Autowired即使在技术上能够通过@Qualifier值引用一个bean名称 ,也不要主要使用它们 。相反,使用@Resource语义定义的JSR-250 注释,通过其唯一名称来标识特定的目标组件,声明的类型与匹配过程无关。@Autowired具有相当不同的语义:按类型选择候选bean后,指定的String限定符值将仅在这些类型选择的候选者中被考虑,例如,将”account”限定符与标记有相同限定符标签的bean相匹配。

对于自己定义为集合/映射或数组类型的@Resource bean ,是一个很好的解决方案,通过唯一的名称引用特定的集合或数组bean。也就是说,从4.3开始,可以通过Spring的@Autowired类型匹配算法来匹配收集/映射和数组类型 ,只要元素类型信息保留在@Bean返回类型签名或集合继承层次结构中即可。在这种情况下,限定符值可用于在相同类型的集合中进行选择,如前一段所述。

截至4.3,@Autowired还考虑注入自引用,即引用回目前注入的bean。注意自我注入是一种后退; 对其他组件的常规依赖性始终优先。在这个意义上,自我参考不参与正式的候选人选择,因此特别是从来没有初级; 相反,他们总是以最低的优先级为最终。在实践中,仅使用自引用作为最后手段,例如通过bean的事务代理在同一实例上调用其他方法:在这种情况下,考虑将受影响的方法分解为单独的委托bean。或者,使用@Resource它可以通过其唯一的名称获取代理回到当前的bean。
@Autowired适用于字段,构造函数和多参数方法,允许在参数级别缩小限定符注释。相比之下,@Resource 仅对具有单个参数的字段和bean属性设置器方法支持。因此,如果您的注入目标是构造函数或多参数方法,请遵守限定词。

您可以创建自己的自定义限定符注释。只需定义注释并@Qualifier在您的定义中提供注释:

@target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre{
    String value();
}

然后,您可以在自动连线字段和参数上提供自定义限定符:

public class MovieRecommender {
    @Autowired 
    @Genre("Action")
    private MovieCatalog actionCatalog;
    private MovieCatalog comedyCatalog;
    @Autowired
    public void setComedyCatalog( @Genre("Comedy") MovieCatalog comedyCatalog){
         this .comedyCatalog = comedyCatalog;
    }
    //...
}

接下来,提供候选bean定义的信息。您可以添加 <qualifier/>标签作为标签的子元素,<bean/>然后指定type并 value匹配您的自定义限定符注释。该类型与注释的完全限定类名匹配。或者,如果没有存在冲突的风险的方便,您可以使用短类名称。这两种方法都在以下示例中进行了说明。

<?xml version ="1.0"encoding ="UTF-8"> 
<beans xmlns = "http://www.springframework.org/schema/beans" 
    xmlnsxsi = "http://www.w3.org /2001 /XMLSchema-instance" 
    xmlnscontext = "http://www.springframework.org/schema/context" 
    xsischemaLocation = "http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd" >
    <context:annotation-config/>
    <bean class = "example.SimpleMovieCatalog" > 
    <qualifier type ="Genre"value ="Action"/>
    < - 注入此bean所需的任何依赖关系 - > 
    </bean>
    <bean class = "example.SimpleMovieCatalog" > 
        <qualifier type ="example.Genre"value ="Comedy"/>
        < - 注入此bean所需的任何依赖关系 - > 
    </bean>
    <bean id = "movieRecommender" class = "example.MovieRecommender" /></bean>

在7.10节”类路径扫描和托管组件”中,您将看到一个基于注释的替代方法,以XML格式提供限定符元数据。具体来说,请参见第7.10.8节”使用注释提供限定符元数据”。
在某些情况下,使用没有值的注释可能就足够了。当注释提供更通用的目的并且可以跨几种不同类型的依赖性应用时,这可能是有用的。例如,您可能会提供一个离线 目录,当没有Internet连接可用时将被搜索。首先定义简单注释:

@target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline{
}

然后将注释添加到要自动连线的字段或属性中:

public class MovieRecommender {
    @Autowired 
    @Offline
    private MovieCatalog offlineCatalog;
    //...
}

现在bean定义只需要一个限定符type:

<bean class = "example.SimpleMovieCatalog" > 
    <qualifier type ="Offline"/>
    < - 注入此bean所需的任何依赖关系 - > </bean>

您还可以定义接受命名属性的自定义限定符注释,而不是简单value属性。如果在要自动连线的字段或参数上指定多个属性值,则bean定义必须与所有这些属性值相匹配 ,以被视为自动连线候选。作为示例,请考虑以下注释定义:

@target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {
    String genre();
    Format format();}

在这种情况下Format是一个枚举:

public enum Format {
    VHS,DVD,BLURAY
}

要自动连线的字段用自定义限定符注释,并包含两个属性的值:genre和format。

public class MovieRecommender {
    @Autowired 
    @MovieQualifier(format = Format.VHS,genre ="Action")
    private MovieCatalog actionVhsCatalog;
    @Autowired 
    @MovieQualifier(format = Format.VHS,genre ="Comedy")
    private MovieCatalog comedyVhsCatalog;
    @Autowired 
    @MovieQualifier(format = Format.DVD,genre ="Action")
    private MovieCatalog actionDvdCatalog;
    @Autowired 
    @MovieQualifier(format = Format.BLURAY,genre ="Comedy")
    private MovieCatalog comedyBluRayCatalog;
    //...
}

最后,bean定义应该包含匹配的限定符值。此示例还说明可以使用bean 元属性而不是 <qualifier/>子元素。如果可用,则<qualifier/>该属性及其属性优先,但<meta/>如果不存在此限定符,则自动布线机制将落在标记中提供的值上 ,如以下示例中的最后两个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:context = "http://www.springframework.org/schema/context" 
    xsi:schemaLocation = "http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd" >
    <context:annotation-config/>
    <bean class = "example.SimpleMovieCatalog" > 
    <qualifier type = "MovieQualifier" > 
        <attribute key = "format" value = "VHS" /> 
        <attribute key = "genre" value = "Action" /> 
    </qualifier> 
    <! - 注入此bean所需的任何依赖关系 - > 
    </bean>
    <bean class = "example.SimpleMovieCatalog" > 
    <qualifier type = "MovieQualifier" > 
            <attribute key = "format" value = "VHS" /> 
        <attribute key = "genre" value = "Comedy" /> 
    </qualifier> 
    <! - 注入此bean所需的任何依赖关系 - > 
    </bean>
    <bean class = "example.SimpleMovieCatalog" > 
    <meta key = "format" value = "DVD" /> 
    <meta key = "genre" value = "Action" /> 
    <! - 注入此bean所需的任何依赖关系 - - > 
    </bean>
    <bean class = "example.SimpleMovieCatalog" > 
    <meta key = "format" value = "BLURAY" /> 
    <meta key = "genre" value = "Comedy" /> 
    <! - 注入此bean所需的任何依赖关系 - - > 
    </bean></beans>

7.9.5使用泛型作为自动布线限定符

除了@Qualifier注释之外,还可以使用Java通用类型作为隐式形式的限定。例如,假设您具有以下配置:

@Configuration
 public class MyConfiguration {
    @Bean
    public StringStore stringStore(){
         return new StringStore();
    }
    @Bean
    public IntegerStore integerStore(){
         return new IntegerStore();
    }
}

假设上述bean实现一个通用接口,即Store<String>Store<Integer>,你可以@Autowire在Store界面和通用将作为一个限定:

@Autowired
private Store<String> s1; //<String>限定符,注入stringStore bean
@Autowired
private Store<Integer> s2; //<整数>限定符,注入integerStore bean

//自动连线列表,map和数组时,通用限定词也适用:
//注入所有存储bean,只要它们有一个<整数>通用//存储<String> bean将不会出现在此列表中@Autowired 
private List <Store <Integer >> s;

7.9.6 CustomAutowireConfigurer

这 CustomAutowireConfigurer 是一个BeanFactoryPostProcessor使您能够注册自己的自定义限定符注释类型,即使它们没有使用Spring的@Qualifier注释注释。

<bean id = "customAutowireConfigurer" 
    class = "org.springframework.beans.factory.annotation.CustomAutowireConfigurer" > 
    <property name = "customQualifierTypes" > 
    <set> 
       <value> example.CustomQualifier </value> 
    </set> 
    </property> </bean>

在AutowireCandidateResolver由确定自动装配候选:
autowire-candidate每个bean定义 的值
元素上 default-autowire-candidates可用的 任何模式<beans/>
@Qualifier注释 的存在和任何注册的自定义注释CustomAutowireConfigurer
当多个bean符合自动线路候选项时,”主要”的确定如下:如果候选者中只有一个bean定义具有primary 属性设置true,则将其选中。

7.9.7 @Resource

Spring还支持使用JSR-250 @Resource注释对字段或bean属性设置器方法进行注入。这是Java EE 5和6中的常见模式,例如在JSF 1.2托管bean或JAX-WS 2.0端点中。Spring也支持Spring管理对象的这种模式。
@Resource取一个name属性,默认情况下,Spring将该值解释为要注入的bean名称。换句话说,它遵循名称语义,如本例所示:

public class SimpleMovieLister {
    private MovieFinder movieFinder;
    @Resource(name ="myMovieFinder")
    public void setMovieFinder(MovieFinder movieFinder){
         this .movieFinder = movieFinder;
    }
}

如果没有明确指定名称,则默认名称来自字段名称或setter方法。在一个字段的情况下,它需要字段名称; 在setter方法的情况下,它将使用bean属性名称。所以下面的例子是将名为”movieFinder”的bean注入到其setter方法中:

public class SimpleMovieLister {
    private MovieFinder movieFinder;
    @Resource
    public void setMovieFinder(MovieFinder movieFinder){
         this .movieFinder = movieFinder;
    }
}

【注】提供注解的名称解析由一个bean的名称 ApplicationContext,其中的CommonAnnotationBeanPostProcessor知道。如果您SimpleJndiBeanFactory 明确配置Spring,可以通过JNDI解析名称 。但是,建议您依赖默认行为,只需使用Spring的JNDI查找功能来保留间接级别。
在专属情况下,@Resource不指定明确的名称,以及类似的使用@Autowired,@Resource发现的主要类型的比赛,而不是一个具体的bean并解决众所周知的解析依存关系:BeanFactory, ApplicationContext,ResourceLoader,ApplicationEventPublisher,和MessageSource 接口。
因此,在以下示例中,该customerPreferenceDao字段首先查找名为customerPreferenceDao的bean,然后返回到该类型的主类型匹配 CustomerPreferenceDao。”上下文”字段是基于已知的可解析依赖类型注入的ApplicationContext。

public class MovieRecommender {
    @Resource
    private CustomerPreferenceDao customerPreferenceDao;
    @Resource
    private ApplicationContext context;
    public MovieRecommender(){
    }
    //...
}

7.9.8 @PostConstruct和@PreDestroy

将CommonAnnotationBeanPostProcessor不仅承认了@Resource注解也是JSR-250 的生命周期注解。在Spring 2.5中引入,对这些注释的支持提供了在初始化回调和 销毁回调中描述的另一种替代 方法。如果 CommonAnnotationBeanPostProcessor在Spring中注册 ApplicationContext,那么在生命周期中与相应的Spring生命周期接口方法或明确声明的回调方法相同的方式调用携带其中一个注释的方法。在下面的示例中,缓存将在初始化时预先填充,并在销毁后清除。

publicclass CachingMovieLister {
    @PostConstruct
    public void populateMovieCache(){
         //初始化时填充影片缓存...
    }
    @PreDestroy
    public void clearMovieCache(){
         //销毁影片缓存时清除...
    }
}

猜你喜欢

转载自blog.csdn.net/sunrainamazing/article/details/77341096
今日推荐