在配置Spring时,注释比XML更好吗?
基于注释的配置的引入提出了这样一个问题:这种方法是否比XML“更好”。简短的回答是“视情况而定”。长话短说,每种方法都有其优缺点,通常由开发人员决定哪种策略更适合他们。由于其定义方式,注释在其声明中提供了大量上下文,从而使配置更短、更简洁。但是,XML擅长在不修改源代码或重新编译组件的情况下连接组件。一些开发人员倾向于让连接靠近源,而另一些人则认为带注释的类不再是pojo,而且配置变得分散且更难控制。
无论选择,spring可以容纳两种风格,甚至混合在一起。值得指出的是,通过它的JavaConfig选项,Spring允许以非侵入性的方式使用注释,而不涉及目标组件的源代码,而且,就工具而言,Spring Tools for Eclipse.支持所有配置样式。
XML设置的另一种替代方法是基于注释的配置,它依赖于字节码元数据来连接组件,而不是使用尖括号声明。开发人员不使用XML来描述bean连接,而是通过在相关的类、方法或字段声明上使用注释将配置移动到组件类本身。如示例中所述:RequiredAnnotationBeanPostProcessor与注释一起使用BeanPostProcessor是扩展Spring IoC容器的一种常见方法。例如,Spring 2.0引入了使用@Required注释强制执行必需属性的可能性。Spring 2.5使遵循相同的通用方法来驱动Spring的依赖项注入成为可能。从本质上讲,@Autowired注解提供了与Autowiring协作器中描述的相同的功能,但是拥有更细粒度的控制和更广泛的适用性。Spring 2.5还增加了对JSR-250注释的支持,比如@PostConstruct和@PreDestroy。Spring 3.0增加了对javax中包含的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>
(隐式注册的后处理器包括AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor,和前面提到的RequiredAnnotationBeanPostProcessor。)
<context:annotation-config/>只在定义它的应用程序上下文中的bean上查找注释。这意味着,如果你在WebApplicationContext中为DispatcherServlet添加<context:annotation-config/>,它只检查控制器中的@Autowired bean,而不是服务。有关更多信息,请参阅DispatcherServlet。
1.9.1 @Require
@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. Using @Autowired
在本节包含的示例中,JSR 330的@Inject注释可以用来替代Spring的@Autowired注释。详情请看这里。
你可以对构造函数应用@Autowired注解,如下面的例子所示:
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
从Spring Framework 4.3开始,如果目标bean只定义了一个构造函数,就不再需要在构造函数上使用@Autowired注解。但是,如果有几个构造函数可用,并且没有主/默认构造函数,那么至少有一个构造函数必须用@Autowired注解,以指示容器使用哪一个。有关构造函数解析的详细信息,请参见讨论。
你也可以对传统的setter方法应用@Autowired注解,如下例所示:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
你也可以将注释应用到具有任意名称和多个参数的方法上,如下面的例子所示:
ublic 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)由您为@ autowire注释的注入点使用的类型一致地声明。否则,注入可能会由于运行时出现“没有找到类型匹配(no type match found)”错误而失败。
对于通过类路径扫描找到的xml定义的bean或组件类,容器通常预先知道具体的类型。但是,对于@Bean工厂方法,您需要确保声明的返回类型具有足够的表达能力。对于实现多个接口的组件,或者对于可能由其实现类型引用的组件,考虑在您的工厂方法上声明最特定的返回类型(至少与引用bean的注入点所要求的那样特定)。
您还可以通过向需要该类型数组的字段或方法添加@Autowired注解来指示Spring从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级别不可用,因为它不能在方法上声明。它的语义可以通过在每种类型的单个bean上结合@Order值和@Primary来建模。
即使是类型化的Map实例也可以自动实现,只要期望的键类型是String。映射值包含所有期望类型的bean,键包含相应的bean名称,如下面的示例所示:
public class MovieRecommender {
private Map<String, MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
默认情况下,当给定注入点没有匹配的候选bean可用时,自动装配将失败。对于声明的数组、集合或映射,至少需要一个匹配元素。
默认行为是将带注释的方法和字段视为指示所需的依赖项。您可以像下面的示例中所演示的那样更改此行为,从而使框架可以通过将不可满足的注入点标记为非必需的(即,通过将@Autowired表格中的必需属性设置为false):
ublic class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired(required = false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
如果一个非必需的方法的依赖项(或者它的一个依赖项,在有多个参数的情况下)不可用,那么它将根本不会被调用。在这种情况下,将不会填充非必需字段,保留其默认值。注入的构造函数和工厂方法参数是一种特殊情况,因为由于Spring的构造函数解析算法可能会处理多个构造函数,@Autowired中的必需属性有不同的含义。默认情况下,构造函数和工厂方法参数是有效的,但在单构造函数场景中有一些特殊规则,比如如果没有匹配的bean可用,则多元素注入点(数组、集合、映射)解析为空实例。这允许一种常见的实现模式,在这种模式中,所有依赖项都可以在一个唯一的多参数构造函数中声明——例如,在没有@Autowired注解的情况下,声明为一个单一的公共构造函数。
在任何给定bean类中,只有一个构造函数可以声明@Autowired,并将required属性设置为true,以指示当作为Spring bean使用时要自动装配的构造函数。因此,如果required属性的默认值为true,那么只有一个构造函数可以使用@Autowired注解。如果有多个构造函数声明注释,那么它们都必须声明required=false,才能被认为是自动装配的候选者(类似于XML中的autowire=constructor)。通过在Spring容器中匹配bean可以满足的依赖关系最多的构造函数将被选择。如果没有一个候选函数可以满足,那么将使用主/默认构造函数(如果存在)。类似地,如果一个类声明了多个构造函数,但是没有一个用@Autowired标注,那么就会使用一个主/默认构造函数(如果存在的话)。如果一个类一开始只声明了一个构造函数,那么它总是会被使用,即使没有注释。请注意,带注释的构造函数不必是公共的。
或者,您可以通过Java 8的Java .util表示特定依赖项的非必需性质。可选,如下例所示:
public class SimpleMovieLister {
@Autowired
public void setMovieFinder(Optional<MovieFinder> movieFinder) {
...
}
}
在Spring Framework 5.0中,还可以使用@Nullable注释(任何包中的任何类型——例如javax.annotation)。或者只是利用Kotlin内置的空安全支持:
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或Spring @Bean方法显式地“连接起来”。
1.9.3 使用@Primary微调基于注释的自动装配
由于按类型自动装配可能会导致多个候选,因此通常有必要对选择过程进行更多的控制。实现这一点的一种方法是使用Spring的@Primary注释。@Primary表示当多个bean是要自动传递到单值依赖项的候选bean时,应该优先给予特定bean。如果候选对象中恰好有一个主bean,它就成为自动生成的值。
指示当多个候选者有资格自动装配依赖项时,应优先考虑bean。此注解在语义上就等同于在Spring XML中定义的bean 元素的primary属性。注意: 除非使用component-scanning进行组件扫描,否则在类级别上使用@Primary不会有作用。如果@Primary 注解定义在XML中,那么@Primary 的注解元注解就会忽略,相反使用
考虑以下配置,它将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"
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 微调基于注释的自动装配,使用限定符
当一个主要候选对象可以被确定时,@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"/> (1)
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier value="action"/>(2)
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
- 具有主限定符值的bean与使用相同值限定的构造函数参数连接。
- 具有动作限定符值的bean与使用相同值限定的构造函数参数连接。
对于回退匹配,bean名称被视为默认的限定符值。因此,您可以使用id为main而不是嵌套的qualifier元素来定义bean,从而得到相同的匹配结果。然而,尽管您可以使用这种约定来按名称引用特定的bean,但@Autowired基本上是关于类型驱动的注入,带有可选的语义限定符。这意味着,即使使用bean名称回退,限定符值也总是在类型匹配集中具有收缩语义。它们在语义上不表示对惟一bean id的引用。好的限定符值是main、EMEA或persistent,表示独立于bean id的特定组件的特征,在匿名bean定义(如前面示例中的bean定义)的情况下,bean id可以自动生成。
限定符也适用于类型化集合,如前所述—例如,用于设置<MovieCatalog>。在这种情况下,根据声明的限定符,所有匹配的bean都作为一个集合注入。这意味着限定符不必是惟一的。相反,它们构成了过滤标准。例如,您可以定义具有相同限定符值“action”的多个MovieCatalog bean,所有这些bean都被注入到带有@Qualifier(“action”)注释的<MovieCatalog>集合中。
让限定符值在类型匹配候选中针对目标bean名称进行选择,不需要在注入点使用@Qualifier注释。如果没有其他解析指示器(例如限定符或主标记),对于非唯一依赖情况,Spring将注入点名称(即字段名或参数名)与目标bean名称匹配,并选择同名的候选对象(如果有的话)。
也就是说,如果您打算通过名称表示注释驱动的注入,请不要主要使用@Autowired,即使它能够通过bean名称在类型匹配的候选者中进行选择。相反,使用JSR-250 @Resource注释,它在语义上定义为通过惟一名称标识特定的目标组件,声明的类型与匹配过程无关。@Autowired有相当不同的语义:在按类型选择候选bean之后,指定的字符串限定符值只在那些类型选择的候选者中被考虑(例如,将一个帐户限定符与用相同限定符标签标记的bean相匹配)。
对于本身定义为集合、映射或数组类型的bean, @Resource是一个很好的解决方案,它通过惟一的名称引用特定的集合或数组bean。也就是说,在4.3,集合中,您也可以通过Spring的@Autowired类型匹配算法来匹配Map和数组类型,只要元素类型信息在@Bean返回类型签名或集合继承层次结构中保留。在这种情况下,您可以使用限定符值在相同类型的集合中进行选择,如前一段所述。
在4.3中,@Autowired也考虑注入的自我引用(也就是说,对当前注入的bean的引用)。注意,self注入是一种退路。对其他组件的常规依赖始终具有优先级。从这个意义上说,自我参照不参与候选人的定期选择,因此,尤其从来没有初选。相反,它们总是优先级最低的。在实践中,您应该仅将self引用用作最后的手段(例如,通过bean的事务代理调用同一实例上的其他方法)。在这种情况下,考虑将受影响的方法分解为单独的委托bean。或者,您可以使用@Resource,它可以通过当前bean的唯一名称获得回它的代理。
尝试在同一个配置类上注入来自@Bean方法的结果实际上也是一个自引用场景。要么在实际需要的方法签名中惰性地解析这些引用(与配置类中的自动生成字段相反),要么将受影响的@Bean方法声明为静态方法,将它们与包含的配置类实例及其生命周期解耦。否则,只在回退阶段考虑这样的bean,而选择其他配置类上的匹配bean作为主要候选对象(如果可用)。
@Autowired适用于领域,构造器,和多参数方法,允许通过限定符注解在参数级别上缩小范围。相比之下,@Resource只支持具有单个参数的字段和bean属性设置器方法。因此,如果注入目标是构造函数或多参数方法,则应该坚持使用限定符。
您可以创建自己的自定义限定符注释。为此,定义一个注释并在定义中提供@Qualifier注释,如下例所示:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
然后你可以在autowired字段和参数上提供自定义限定符,如下面的例子所示:
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/>标记的子元素,然后指定类型和值来匹配您的自定义qualifier注释。类型与注释的全限定类名匹配。另外,为了方便起见,如果不存在名称冲突的风险,您可以使用简短的类名。下面的例子演示了这两种方法:
<?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 这一行添加了@Offline注释。
private MovieCatalog offlineCatalog;
// ...
}
现在,bean定义只需要一个限定符类型,如下面的示例所示:
<bean class="example.SimpleMovieCatalog">
<qualifier type="Offline"/> 此元素指定限定符。
<!-- inject any dependencies required by this bean -->
</bean>
您还可以定义自定义限定符注释,这些注释除了简单的value属性外,还接受指定的属性。如果在要自动装配的字段或参数上指定了多个属性值,那么bean定义必须匹配所有这些属性值,才能被认为是自动装配的候选者。作为一个例子,考虑下面的注释定义:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {
String genre();
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泛型类型作为一种
private List<Store<Integer>> s;
隐式的限定形式。例如,假设您有以下配置:
@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
泛型限定符在自动装配列表、映射实例和数组时也适用。下面的例子自动生成一个通用列表:
private List<Store<Integer>> s;
1.9.6。使用CustomAutowireConfigurer
ustomautowiresfigurer是一个BeanFactoryPostProcessor,它允许您注册自己的自定义限定符注释类型,即使它们没有使用Spring的@Qualifier注释。下面的例子展示了如何使用customautowiresfigurer:
<bean id="customAutowireConfigurer"
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>example.CustomQualifier</value>
</set>
</property>
</bean>
AutowireCandidateResolver通过以下方式确定autowire候选者:
- 每个bean定义的自动候选值
- <beans/>元素上可用的任何默认自动候选模式
- @Qualifier注解和任何在customautowiresfigurer注册的自定义注解的存在
当多个bean符合自动装配候选时,确定“主”的方法如下:如果候选中恰好有一个bean定义的主属性设置为true,则选择它。
1.9.7。注入用@Resource
Spring还通过在字段或bean属性设置器方法上使用JSR-250 @Resource注释来支持注入。这是Java EE中的常见模式:例如,在jsf管理的bean和JAX-WS端点中。Spring也为Spring管理的对象支持此模式。
@Resource具有name属性。默认情况下,Spring将该值解释为要注入的bean名。换句话说,它遵循了名称语义,如下面的示例所示:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder") 这一行注入一个@Resource
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; 上下文字段是基于已知的可解析依赖类型注入的:ApplicationContext。
public MovieRecommender() {
}
// ...
}
1.9.8. Using @Value
@Value通常用于注入外部化属性:
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("${catalog.name}") String catalog) {
this.catalog = catalog;
}
}
配置如下:
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }
和下面的应用程序。属性文件:
catalog.name=MovieCatalog
译注:有时候读取xml文件时候会报错:文档根元素 "beans" 必须匹配 DOCTYPE 根 "null"。可以改成@ImportSource试试
在这种情况下,目录参数和字段将等于MovieCatalog值。
Spring提供了缺省的宽松嵌入式值解析器。它将尝试解析属性值,如果无法解析,属性名(例如${catalog.name})将作为值注入。如果你想严格控制不存在的值,你应该声明一个PropertySourcesPlaceholderConfigurer bean,如下面的例子所示:
@Configuration
public class AppConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
当使用JavaConfig配置PropertySourcesPlaceholderConfigurer时,@Bean方法必须是静态的
如果无法解决任何${}占位符,使用上述配置将确保Spring初始化失败。也可以使用setPlaceholderPrefix、setPlaceholderSuffix或setValueSeparator等方法来定制占位符。
Spring引导默认配置一个PropertySourcesPlaceholderConfigurer bean,该bean将从应用程序中获取属性。属性和应用程序。yml文件。
Spring提供的内置转换器支持允许自动处理简单类型转换(例如到Integer或int)。多个逗号分隔的值可以自动转换为字符串数组,而不需要额外的努力。
可以提供如下默认值:
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) {
this.catalog = catalog;
}
}
Spring BeanPostProcessor在后台使用ConversionService来处理将@Value中的字符串值转换为目标类型的过程。如果你想为自己的自定义类型提供转换支持,你可以提供自己的ConversionService bean实例,如下面的例子所示:
@Configuration
public class AppConfig {
@Bean
public ConversionService conversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
conversionService.addConverter(new MyCustomConverter());
return conversionService;
}
}
当@Value包含SpEL表达式时,该值将在运行时动态计算,如下例所示:
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) {
this.catalog = catalog;
}
}
SpEL还允许使用更复杂的数据结构:
@Component
public class MovieRecommender {
private final Map<String, Integer> countOfMoviesPerCatalog;
public MovieRecommender(
@Value("#{{'Thriller': 100, 'Comedy': 300}}") Map<String, Integer> countOfMoviesPerCatalog) {
this.countOfMoviesPerCatalog = countOfMoviesPerCatalog;
}
}
1.9.9。使用@PostConstruct和@PreDestroy
CommonAnnotationBeanPostProcessor不仅识别@Resource注释,还识别JSR-250生命周期注释:javax.annotation。PostConstruct javax.annotation.PreDestroy。在spring2.5中引入的对这些注释的支持为初始化回调和销毁回调中描述的生命周期回调机制提供了另一种选择。如果在Spring ApplicationContext中注册了CommonAnnotationBeanPostProcessor,那么在生命周期中与对应的Spring生命周期接口方法或显式声明的回调方法在同一点调用携带这些注释之一的方法。在下面的例子中,缓存在初始化时被预填充,在销毁时被清除:
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。JDK 9中的注释包与核心Java模块分离,最终在JDK 11中被删除。如果需要,可以使用javax。现在需要通过Maven Central获得注释api构件,只需像其他库一样将其添加到应用程序的类路径。