1.9 基于注解的容器配置

注解是不是比XML配置更好?

引入基于注解的配置提出了这样一个问题:这种方法是否比XML“更好”。

简短的答案是“它取决于”。

长的答案是:每种方法都有其优缺点,通常情况下,由开发人员决定哪种策略更适合它们。

由于它们的定义方式,注解在其声明中提供了大量上下文,从而导致配置更简短。

然而,XML擅长在不接触源代码或重新编译组件的情况下注入组件。

一些开发人员更喜欢将注入靠近源代码,而其他人则认为带注解的类不再是POJO,而且配置变得分散且难以控制。

无论选择哪一种,Spring都能容纳这两种风格,甚至可以将它们混合在一起。

值得指出的是,通过它的JavaConfig选项,Spring允许以非侵入性的方式使用注解,而不接触目标组件源代码,并且在工具方面,所有配置样式都由Spring工具套件支持。

基于注解的配置提供了XML设置的替代方案,它依赖字节码元数据来注入组件,而不是使用尖括号声明。

开发人员不使用XML来描述bean连接,而是使用相关类、方法或字段声明上的注解将配置移动到组件类本身。

如示例中所述:RequiredAnnotationBeanPostProcessor与注解结合使用BeanPostProcessor是扩展SpringIOC容器的常用方法。

例如,Spring2.0引入了@Required注解,标记属性必须存在。

Spring2.5使得遵循相同的方法来驱动Spring的依赖注入成为可能。

本质上,@Autowired注解提供了与Autowiring Collaborators中描述的相同的功能,但是具有更细粒度的控制和更广泛的适用性。

Spring2.5还增加了对JSR-250注解的支持,如@PostConstruct@PreDstroy

Spring 3为javax.inject包中包含的JSR-330(Java依赖注入)注解添加了支持,例如@Inject@Named。有关这些注解的详细信息,请参阅相关部分。

注解注入在XML注入之前执行。因此,当两个同时使用时,XML配置会覆盖注解注入的属性。

同样的,你可以将它们注册为单个bean定义,但也可以通过在基于XML的Spring配置中包含以下标记来隐式注册它们(注意上下文命名空间的包含):

<?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">

    <context:annotation-config/>

</beans>

(隐式注册的post-processors包括AutoWiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorPersistenceAnnotationBeanPostProcessor和上述RequiredAnnotationBeanPostProcessor。)

<context:annotation-config/>仅在定义bean的同一应用程序上下文中查找bean上的注解。

这意味着,如果在DispatcherServletWebApplicationContext中放置<context:annotation-config/>,它只检查控制器中的@Autowired bean,而不检查服务。有关详细信息,请参阅DispatcherServlet

1.9.1 @Required

@Required注解适用Bean的setter方法。如下:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Required
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

此注解指示受影响的bean属性必须在配置时通过bean定义中的显式属性值或通过自动装载填充。

如果未填充受影响的bean属性,则容器将引发异常。这就允许了早期和显式的失败,避免了后面的NullPointerException实例等。我们仍然建议你将断言放入bean类本身(例如,放到init方法中)。这样做会强制那些必需的引用和值,即使在容器外部使用类也是如此。

从Spring Framework 5.1开始,@Required注解正式被弃用,取而代之的是将构造函数注入用于所需的设置(或使用InitializingBean.afterPropertiesSet()的自定义实现以及bean属性setter方法)。

1.9.2 使用 @Autowired

在本节包含的示例中,JSR 330的@Inject注解可以代替spring的@Autowired注解。有关详细信息,请参阅此处。

你可以将@Autowired注解到构造器中,如下所示:

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

从SpringFramework4.3开始,如果目标bean只定义了一个构造函数,那么就不再需要在此类构造函数上使用@Autowired注解。但是,如果有多个构造函数可用,则必须至少对其中一个进行注解,以告诉容器使用哪一个。

@Autowired也可以注解到传统的setter方法,如下例子所示:

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;
    }

    // ...
}

确保目标组件(例如movieCatalogcustomerPreferenceDAO)始终通过类型来声明,这样才能用在@Autowired注入点。否则,由于在运行时找不到类型匹配,注入可能会失败。

对于通过类路径扫描找到的XML定义的bean或组件类,容器通常预先知道具体的类型。但是,对于@Bean工厂方法,你需要确保声明的返回类型具有足够的表现力。对于实现多个接口的组件或可能由其实现类型引用的组件,考虑在工厂方法上声明最特定的返回类型(至少与引用bean的注入点所要求的特定类型相同)。

还可以通过将注解添加到需要该类型数组的字段或方法,那么可以从ApplicationContext中获取到该特定类型的所有bean,如下例所示:

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注解。

否则,它们的顺序遵循容器中相应目标bean定义的注册顺序。

你可以在目标类级别和@Bean方法上声明@Order注解,可能是通过单个bean定义(在使用同一bean类的多个定义的情况下)声明的。@Order可能会影响注入点的优先级,但请注意,它们不会影响单例启动顺序,这是一个由依赖关系和@DependsOn声明确定的正交问题。

注意,标准javax.annotation.Priority注解在@Bean级别不可用,因为它不能在方法上声明。它的语义可以通过@Order 的values和@Primary在每种类型的单个bean上进行建模。

Map实例也可以被注入,只要key是String类型。Map value包括了所有的类型匹配的Bean,keys是该bean的名字。

如下所示:

public class MovieRecommender {

    private Map<String, MovieCatalog> movieCatalogs;

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

    // ...
}

如果注入点没有可以匹配的目标,那么自动注入会失败。如果是array, collection 或者 map,至少要有一个元素能匹配。

默认行为是将带注解的方法和字段视为指示所需依赖项。你可以如以下示例中所示更改此行为,使框架能够跳过不可满足的注入点,方法是将其标记为非必需:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}

如果非必需方法的依赖项(或多个参数的依赖项之一)不可用,则根本不会调用该方法。

在这种情况下,不需要填写的字段将不会被填充,从而保留其默认值。

注入的构造函数和工厂方法参数是一种特殊情况,因为@Autowired上的“required”标志的含义有所不同,因为Spring的构造函数解析算法可能处理多个构造函数。默认情况下,构造函数和工厂方法参数实际上是必需的,但在单个构造函数场景中有一些特殊规则,例如,如果没有匹配的bean可用,多元素注入点(数组、集合、映射)解析为空实例。这允许使用一个通用的实现模式,其中所有依赖项都可以在唯一的多参数构造函数中声明,例如声明为没有@Autowired注解的单个公共构造函数。

每个类只能标记一个带required注解的构造函数,但可以标记多个非必需的构造函数。在这种情况下,每一个都是候选对象,Spring将使用最贪婪的可以满足最多依赖关系的构造函数,也就是说,拥有最多参数的构造函数。构造函数解析算法与使用重载构造函数的未注解类相同,只是将候选对象缩小到带注解的构造函数。

建议使用@Autowired的'required'属性而不是使用setter方法上的@Required注解。“required”属性表示自动装载不需要该属性。如果无法自动装载,则忽略该属性。另一方面,@Required更强大,因为它强制通过容器支持的任何方式来设置属性。如果未定义任何值,则会引发相应的异常。

或者,你可以通过Java 8的java.util.Optional表示特定依赖项的非必需性质,如下示例显示:

public class SimpleMovieLister {

    @Autowired
    public void setMovieFinder(Optional<MovieFinder> movieFinder) {
        ...
    }
}

在Spring Framework 5.0中,你也可以使用@Nullable注解(任何包中的任何类型,例如,JSR-305中的javax.annotation.Nullable):

public class SimpleMovieLister {

    @Autowired
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
        ...
    }
}

你还可以使用@Autowired处理已知可解析依赖项的接口:BeanFactoryApplicationContextEnvironmentResourceLoaderApplicationEventPublisherMessageSource。这些接口及其扩展接口(如ConfigurableApplicationContextResourcePatternResolver)将自动解析,无需特殊设置。

以下示例自动装载ApplicationContext对象:

public class MovieRecommender {

    @Autowired
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...
}

@Autowired, @Inject, @Value, 和 @Resource 注解是在Spring的BeanPostProcessor中处理的,这意味着你不能将这些注解用在你自己的BeanPostProcessorBeanFactoryPostProcessor类型。这些类型必须使用XML或者@Bean方法来显示指定。

1.9.3 使用@primary进行基于微调注解的自动装载

由于按类型自动装载可能会导致多个候选者,因此通常需要对选择过程进行更多的控制。

实现这一点的一种方法是使用Spring的@Primary注解。

@Primary表示当多个bean是要自动连接到单值依赖项的候选对象时,应该优先考虑特定bean。

如果候选对象中只存在一个主bean,则它将成为自动装载的值。

考虑将firstMovieCatalog定义为Primary 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"
    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">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog" primary="true">
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

1.9.4 带有Qualifier的基于微调注解的自动装载

当可以确定一个主要候选对象时,@Primary是一种在多个实例中按类型使用自动装载的有效方法。当你需要对选择过程进行更多控制时,可以使用Spring的@Qualifier注解。你可以将限定符值特定参数相关联,缩小类型匹配集的范围,以便为每个参数选择特定的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定义:

<?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">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="main"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="action"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

对于回退匹配,bean名称被视为默认限定符值。因此,可以使用id为main而不是嵌套限定符元素来定义bean,从而得到相同的匹配结果。

然而,尽管你可以使用此约定按名称引用特定的bean,@Autowired是关于带有可选语义限定符的类型驱动注入的根本。这意味着,即使使用bean名称回退,限定符值在类型匹配集内也始终具有收缩语义。它们不会在语义上表示对唯一bean id的引用。好的限定符值比如main、EMEA或persistent,表示独立于bean id的特定组件的特征,在匿名bean定义(如前面示例中的定义)的情况下,可以自动生成这些特征。

限定符也适用于类型化的集合,如前面所述(例如,适用于Set <MovieCatalog>)。在这种情况下,根据声明的限定词,所有匹配的bean都作为一个集合注入。这意味着限定词不必是唯一的。相反,它们构成了过滤标准。例如,您可以使用相同的限定符值“ action”定义多个MovieCatalog Bean,所有这些都注入到注有@Qualifier(“ action”)Set <MovieCatalog>中。

让限定符值根据目标bean名称在类型匹配候选中进行选择,不需要在注入点处使用@Qualifier注解。如果没有其他分辨标记(例如限定符或主标记),对于非唯一依赖关系情况,Spring将针对目标bean名称匹配注入点名称(即字段名称或参数名称),来选择相同的命名候选对象(如果有)。

也就是说,如果你打算用名称表示注解驱动的注入,那么不要使用@Autowired,即使它能够在类型匹配的候选对象中通过bean名称进行选择。相反,使用JSR-250 @Resource注解,该注解在语义上定义为通过其唯一名称标识特定的目标组件,声明的类型与匹配过程无关。a

@Autowired有相当不同的语义:在按类型选择候选bean之后,指定的字符串限定符值只在那些类型选择的候选对象中考虑(例如,将account限定符与标记有相同限定符标签的bean匹配)。

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

从4.3开始,@ Autowired还考虑了自我引用以进行注入(即,引用回当前注入的Bean)。请注意,自我注入是一个后备。对其他组件的常规依赖始终优先。从这个意义上说,自我推荐不参与常规的候选人选择,因此尤其是绝不是主要的。相反,它们总是以最低优先级结束。实际上,您应该仅将自我引用用作最后的手段(例如,通过Bean的事务代理在同一实例上调用其他方法)。考虑在这种情况下将受影响的方法分解为单独的委托bean。或者,您可以使用@Resource,它可以通过其唯一名称获取返回到当前bean的代理。

@Autowired应用于字段、构造函数和多参数方法,允许在参数级别缩小限定符注解的范围。相反,只有具有单个参数的字段和bean属性setter方法才支持@Resource。因此,如果注入目标是一个构造函数或多参数方法,那么应该坚持使用限定符。

你可以创建自己的自定义限定符注解。

为此,请定义一个注解并在定义中提供@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定义的信息。您可以将标记添加为标记的子元素,然后指定类型和值以匹配您的自定义限定符注释。该类型与注释的完全限定的类名匹配。

另外,为方便起见,如果不存在名称冲突的风险,则可以使用简短的类名。

下面的示例演示了两种方法:

<?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">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="Genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="example.Genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

在类路径扫描和托管组件中,可以看到基于注解的替代方案,以XML形式提供限定符元数据。

具体来说,请参见为限定符元数据提供注解。

在某些情况下,使用不带值的注解可能就足够了。当注解具有更一般的用途并且可以应用于多个不同类型的依赖项时,这一点非常有用。

例如,你可以提供一个脱机目录,当没有可用的Internet连接时可以搜索该目录。

首先,定义简单注解,如下示例所示:

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

}

然后将注解添加到要自动装载的字段或属性中,如下面的示例所示:

public class MovieRecommender {

    @Autowired
    @Offline 
    private MovieCatalog offlineCatalog;

    // ...
}

现在bean定义只需要一个qualifier type就够了:

<bean class="example.SimpleMovieCatalog">
    <qualifier type="Offline"/> 
    <!-- inject any dependencies required by this bean -->
</bean>

你还可以定义自定义限定符注解,这些注解接受除简单值属性之外的命名属性,或者不接受简单值属性。如果在要自动注入的字段或参数上指定多个属性值,则bean定义必须匹配所有此类属性值,才能被视为自动连线注入。

例如,考虑以下注解定义:

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @Interface MovieQualifier {

    String genre();

    Format format();
}

Format是一个枚举,如下:

public enum Format {
    VHS, DVD, BLURAY
}

要自动注入的字段将使用自定义限定符进行注解,并包含属性(genreformat)的值,如下例所示:

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元属性而不是元素。如果可用,则以元素及其属性为准,但如果不存在这样的限定符,则自动装载机制将返回到标记内提供的值,如下面示例中最后两个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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://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>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="MovieQualifier">
            <attribute key="format" value="VHS"/>
            <attribute key="genre" value="Comedy"/>
        </qualifier>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="DVD"/>
        <meta key="genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="BLURAY"/>
        <meta key="genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

</beans>

1.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> qualifier, injects the stringStore bean

@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean

通用限定符也适用于自动装载列表、MAP实例和数组。以下示例自动装载通用列表:

// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;

1.9.6 使用CustomAutowireConfigurer

CustomAutoWireConfigurer是一个BeanFactoryPostProcessor,它允许你注册自己的自定义限定符注解类型,即使它们没有用Spring的@Qualifier进行注解。

下面的示例演示如何使用CustomAutoWireConfigurer

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

AutowireCandidateResolver通过下面几种方式来确定自动注入的候选人:

  • bean中定义的autowire-candidate
  • default-autowire-candidates模式
  • @Qualifier注解和任何自定义的注册到CustomAutowireConfigurer的注解。

当多个bean符合autowire候选条件时,“primary”的确定如下:如果候选对象中只有一个bean定义的primary属性设置为true,则选择它。

1.9.7 使用@Resource注入

Spring还支持通过在字段或bean属性setter方法上使用jsr-250 @Resource注解(javax.annotation.Resource)进行注入。

这是JavaEE中常见的模式:例如,在JSF托管bean和JAX-WS端点中。Spring也支持Spring管理对象的这种模式。

@Resource具有名称属性。默认情况下,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;
    }
}

注解提供的名称由CommonAnnotationBeanPostProcessor知道的ApplicationContext解析为bean名称。如果显式配置Spring的SimpleJndiBeanFactory,则可以通过JNDI解析名称。但是,我们建议你依赖默认行为,并使用Spring的JNDI查找功能来保持间接寻址的级别。

@Resource用法中,如果没有指定显式名称,并且类似于@Autowired@Resource会找到一个主类型匹配,而不是指定的bean,并解析已知的可解析依赖项:BeanFactory、ApplicationContext、ResourceLoader、ApplicationEventPublisher,和MessageSource接口。

因此,在下面的示例中,customerPreferenceDAO字段首先查找名为“customerPreferenceDAO”的bean,然后返回到与customerPreferenceDAO类型匹配的主类型:

public class MovieRecommender {

    @Resource
    private CustomerPreferenceDao customerPreferenceDao;

    @Resource
    private ApplicationContext context; 

    public MovieRecommender() {
    }

    // ...
}

1.9.8 使用@PostConstruct和@PreDestroy

CommonAnnotationBeanPostProcessor不仅识别@Resource注解,还识别JSR-250生命周期注解:javax.annotation.PostConstructjavax.annotation.PreDestroy

在Spring2.5中引入了对这些注解的支持,它提供了生命周期回调机制的替代方案,如初始化回调和销毁回调中所述。如果CommonAnnotationBeanPostProcessor注册在Spring ApplicationContext中,则在生命周期中与相应的Spring Lifecycle Interface方法或显式声明的回调方法相同的点调用包含这些注解之一的方法。

在以下示例中,缓存在初始化时预填充,在销毁时清除:

public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}

有关组合各种生命周期机制的效果的详细信息,请参见组合生命周期机制。

@Resource一样,@PostConstruct@PreDestroy注解类型是JDK 6到8标准Java库的一部分。然而,整个javax.annotation包与JDK 9中的核心Java模块分离,并最终在JDK 11中被删除。如果需要,javax.annotation-api.jar现在需要通过maven central获得,只需像其他库一样添加到应用程序的类路径中即可。