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定义(如前面示例中的定义)的情况下,可以自动生成这些特征。
限定符也适用于类型化的集合,如前面所述(例如,适用于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
}
要自动注入的字段将使用自定义限定符进行注解,并包含属性(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元属性而不是
<?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.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.jar现在需要通过maven central获得,只需像其他库一样添加到应用程序的类路径中即可。