# 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包括AutoWiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor和上述RequiredAnnotationBeanPostProcessor。）

\<context:annotation-config/> 仅在定义bean的同一应用程序上下文中查找bean上的注解。这意味着，如果在DispatcherServlet的WebApplicationContext中放置\<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;
    }

    // ...
}
```

> 确保目标组件（例如movieCatalog或customerPreferenceDAO）始终通过类型来声明，这样才能用在@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处理已知可解析依赖项的接口：BeanFactory、ApplicationContext、Environment、ResourceLoader、ApplicationEventPublisher和MessageSource。这些接口及其扩展接口（如ConfigurableApplicationContext或ResourcePatternResolver）将自动解析，无需特殊设置。以下示例自动装载ApplicationContext对象：

```
public class MovieRecommender {

    @Autowired
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...
}
```

@Autowired, @Inject, @Value, 和 @Resource 注解是在Spring的BeanPostProcessor中处理的，这意味着你不能将这些注解用在你自己的BeanPostProcessor，BeanFactoryPostProcessor类型。这些类型必须使用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定义（如前面示例中的定义）的情况下，可以自动生成这些特征。

限定符也适用于类型化集合，如前面讨论的-例如，设置。在这种情况下，根据声明的限定符，所有匹配的bean都作为集合注入。这意味着限定符不必是唯一的。相反，它们构成了过滤标准。例如，可以定义多个具有相同限定符值“action”的MovieCatalog bean，所有这些限定符值都被注入到一个用@qualifier（“action”）注解的集合中。

> 让限定符值根据目标bean名称在类型匹配候选中进行选择，不需要在注入点处使用@Qualifier注解。如果没有其他分辨标记（例如限定符或主标记），对于非唯一依赖关系情况，Spring将针对目标bean名称匹配注入点名称（即字段名称或参数名称），来选择相同的命名候选对象（如果有）。
>
> 也就是说，如果你打算用名称表示注解驱动的注入，那么不要主要使用@Autowired，即使它能够在类型匹配的候选对象中通过bean名称进行选择。相反，使用JSR-250 @Resource注解，该注解在语义上定义为通过其唯一名称标识特定的目标组件，声明的类型与匹配过程无关。@Autowired有相当不同的语义：在按类型选择候选bean之后，指定的字符串限定符值只在那些类型选择的候选对象中考虑（例如，将account限定符与标记有相同限定符标签的bean匹配）。
>
> 对于本身定义为集合、映射或数组类型的bean，@Resource是一个很好的解决方案，它通过唯一的名称引用特定的集合或数组bean。也就是说，从4.3版的collection开始，只要元素类型信息保存在@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定义提供信息。可以添加\<qualifier/>标记作为\<bean/>标记的子元素，然后指定type和value以匹配自定义限定符注解。类型与注解的完全限定类名匹配。或者，为了方便起见，如果不存在名称冲突的风险，可以使用短类名。下面的示例演示了这两种方法：

```
<?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
}
```

要自动注入的字段将使用自定义限定符进行注解，并包含属性（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
        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
```

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

```
// 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
* \<beans/>的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.PostConstruct和javax.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工件现在需要通过maven central获得，只需像其他库一样添加到应用程序的类路径中即可。
