# 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获得，只需像其他库一样添加到应用程序的类路径中即可。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.flydean.com/spring-framework-documentation5/core-technologies/1.the-ioc-container/1.9annotation-based-container-configuration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
