在配置Spring时,注释比XML更好吗?
基于注解的配置的引入提出了这种方法是否比XML“更好”的问题。简短的回答是“视情况而定。”简单地说,每种方法都有其优缺点,通常由开发人员决定哪种策略更适合他们。由于它们的定义方式,注解在它们的声明中提供了大量上下文,从而导致更短、更简洁的配置。但是,XML擅长在不涉及源代码或重新编译的情况下将组件连接起来。一些开发人员更喜欢让连接靠近源代码,而另一些开发人员则认为带注解的类不再是pojo,而且,配置变得分散且更难控制。
无论选择什么,Spring都可以适应这两种风格,甚至可以将它们混合在一起。值得指出的是,通过JavaConfig选项,Spring允许以一种非侵入式的方式使用注释,而不涉及目标组件的源代码,而且,就工具而言,所有配置样式都由Spring Tools for Eclipse支持。
基于注解的配置提供了XML设置的替代方案,它依赖于字节码元数据来连接组件,而不是尖括号声明。开发人员不使用XML描述bean连接,而是通过在相关类、方法或字段声明上使用注解将配置移动到组件类本身。如示例:AutowiredAnnotationBeanPostProcessor中所述,将BeanPostProcessor与注解结合使用是扩展Spring IoC容器的常用方法。例如,Spring 2.5引入了一种基于注解的方法来驱动Spring的依赖注入。本质上,@Autowired注解提供了与Autowiring合作者中描述的相同的功能,但具有更细粒度的控制和更广泛的适用性。Spring 2.5还增加了对JSR-250注释的支持,比如@PostConstruct和@PreDestroy。Spring 3.0增加了对jakarta中包含的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>
<context:annotation-config/>元素隐式地注册了以下后处理器:
ConfigurationClassPostProcessorAutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorPersistenceAnnotationBeanPostProcessorEventListenerMethodProcessor
<context:annotation-config/>只在定义它的同一个应用程序上下文中查找bean上的注释。这意味着,如果您将<context:annotation-config/>放在DispatcherServlet的WebApplicationContext中,它只检查控制器中的@Autowired bean,而不是服务中的@Autowired bean。有关更多信息,请参阅DispatcherServlet。
1.9.1. 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注释其中一个构造函数,以便指示容器使用哪个构造函数。有关详细信息,请参阅构造函数解析的讨论。
你也可以将@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的注入点所要求的那样特定)。
您还可以通过将@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声明确定的正交关注点。
注意,标准的jakarta.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中的required属性设置为false):
- 当required属性为true的时候,注入SpringBean的时候,该bean必须存在,不然会注入失败,启动报错
- 当required属性为false的时候,注入SpringBean的时候,如果bean存在,就注入;如果不存在,就忽略跳过,启动不会报错,但不能直接使用
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired(required = false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
如果一个非必需方法的依赖项不可用(如果有多个参数,则它的一个依赖项不可用),则根本不会被调用。在这种情况下,不需要填充非必需字段,只保留其默认值。
换句话说,将必需的属性设置为false表示对应的属性对于自动连接目的是可选的,如果不能自动连接,则将忽略该属性。这允许为属性分配默认值,可以通过依赖注入选择性地覆盖这些值。
注入的构造函数和工厂方法参数是一种特殊情况,因为@Autowired中的required属性具有不同的含义,这是由于Spring的构造函数解析算法可能潜在地处理多个构造函数。构造函数和工厂方法参数在默认情况下是有效的,但在单构造函数场景中有一些特殊的规则,例如,如果没有匹配的bean可用,多元素注入点(数组、集合、映射)将解析为空实例。这允许一种通用的实现模式,其中所有依赖关系都可以在一个唯一的多参数构造函数中声明——例如,声明为一个没有@Autowired注释的单个公共构造函数。
任何给定bean类中只有一个构造函数可以声明@Autowired,并将所需属性设置为true,这表明该构造函数在用作Spring bean时将自动连接。因此,如果所需的属性保留其默认值true,则只有一个构造函数可以使用@Autowired进行注释。如果多个构造函数声明注释,它们都必须声明required=false,以便被视为自动装配的候选者(类似于XML中的autowire=constructor)。可以通过匹配Spring容器中的bean来满足依赖关系数量最多的构造函数将被选择。如果没有一个候选构造函数可以满足,那么将使用主/默认构造函数(如果存在)。类似地,如果一个类声明了多个构造函数,但是没有一个用@Autowired注释,那么将使用主/默认构造函数(如果存在的话)。如果一个类在开始时只声明了一个构造函数,那么它将总是被使用,即使没有注释。注意,带注释的构造函数不一定是公共的。
或者,您可以通过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),或者只是利用Kotlin内置的null安全支持:
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.2. 使用@Primary微调基于注解的自动装配
参考外部文档:www.cnblogs.com/jyy599/p/12…
由于按类型自动装配可能会导致多个候选人,因此通常有必要对选择过程进行更多的控制。实现这一点的一种方法是使用Spring的@Primary注释。@Primary表示当多个bean都是自动连接到单值依赖项的候选bean时,应该优先考虑某个特定bean。如果候选bean中恰好存在一个主bean,则它将成为自动连接的值。
考虑以下配置,它将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.3. 使用qualifier微调基于注解的自动装配
参考文档:www.jianshu.com/p/a2ee8e47e…
当可以确定一个主要候选实例时,@Primary是使用多个实例按类型自动装配的有效方法。当您需要对选择过程进行更多控制时,您可以使用Spring的@Qualifier注释。您可以将限定符值与特定的参数相关联,缩小类型匹配集,以便为每个参数选择特定的bean。在最简单的情况下,这可以是一个简单的描述性值,如下例所示:
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}
你也可以在单独的构造函数参数或方法参数上指定@Qualifier注释,如下例所示:
public class MovieRecommender {
private final MovieCatalog movieCatalog;
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
<?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>
- 带有main限定符值的bean与带有相同限定值的构造函数参数连接在一起。
- 具有操作限定符值的bean与具有相同限定值的构造函数参数连接在一起。
对于回退匹配,bean名被视为默认限定符值。因此,您可以使用main的id来定义bean,而不是嵌套的qualifier元素,从而得到相同的匹配结果。然而,尽管您可以使用这个约定按名称引用特定的bean,但@Autowired基本上是关于带有可选语义限定符的类型驱动注入。这意味着,即使使用bean名回退,限定符值在类型匹配集中也总是具有缩小语义。它们不从语义上表示对惟一bean id的引用。好的限定符值是main、EMEA或persistent,表示独立于bean id的特定组件的特征,在使用匿名bean定义(如前面示例中的bean定义)的情况下,可以自动生成bean id。
如前所述,限定符也适用于类型化集合——例如,Set<MovieCatalog>。在这种情况下,根据声明的限定符,所有匹配的bean都被注入为一个集合。这意味着限定符不必是唯一的。相反,它们构成了过滤标准。例如,您可以定义多个具有相同限定符值“action”的MovieCatalog bean,所有这些bean都被注入到带有@Qualifier(“action”)注释的Set<MovieCatalog>中。
在类型匹配的候选对象中,让限定符值针对目标bean名称进行选择,不需要在注入点上使用@Qualifier注释。如果没有其他解析指示器(如限定符或主标记),对于非唯一依赖情况,Spring将根据目标bean名称匹配注入点名称(即字段名称或参数名称),并选择同名候选(如果有的话)。
也就是说,如果您打算通过名称来表示注释驱动的注入,那么不要主要使用@Autowired,即使它能够在类型匹配候选对象中根据bean名称进行选择。相反,应该使用JSR-250 @Resource注释,该注释在语义上定义为通过唯一名称标识特定的目标组件,声明的类型与匹配过程无关。@Autowired具有完全不同的语义:在按类型选择候选bean之后,只在那些类型选择的候选bean中考虑指定的String限定符值(例如,将帐户限定符与标记有相同限定符标签的bean匹配)。
对于本身定义为集合、Map或数组类型的bean, @Resource是一个很好的解决方案,它通过唯一的名称引用特定的集合或数组bean。也就是说,从4.3开始,只要元素类型信息保存在@Bean返回类型签名或集合继承层次结构中,您也可以通过Spring的@Autowired类型匹配算法来匹配集合、Map和数组类型。在这种情况下,可以使用限定符值在相同类型的集合中进行选择,如前一段所述。
从4.3开始,@Autowired还考虑了注入的自我引用(也就是说,对当前注入的bean的引用)。注意,自我注入是一种退步。对其他组件的常规依赖总是有优先级。从这个意义上说,自我推荐不参与常规的候选人选择,因此尤其不是初选。相反,它们总是以最低优先级结束。在实践中,您应该只将自我引用作为最后的手段(例如,通过bean的事务代理调用同一实例上的其他方法)。在这样的场景中,考虑将受影响的方法分解到单独的委托bean中。或者,您可以使用@Resource,它可以通过当前bean的唯一名称获取一个代理。
尝试将@Bean方法的结果注入到相同的配置类实际上也是一种自引用场景。要么惰性地解析方法签名中实际需要的引用(与配置类中的自动连接字段相反),要么将受影响的@Bean方法声明为静态,将它们与包含的配置类实例及其生命周期分离。否则,这样的bean只在回退阶段被考虑,而其他配置类上的匹配bean则被选择为主要候选bean(如果可用的话)。
@Autowired适用于字段、构造函数和多参数方法,允许在参数级别通过限定符注释进行缩小。相反,@Resource仅支持具有单个参数的字段和bean属性setter方法。因此,如果您的注入目标是构造函数或多参数方法,您应该坚持使用限定符。
您可以创建自己的自定义限定符注解。为此,定义一个注解并在定义中提供@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/>标记的子元素,然后指定类型和值以匹配您的自定义限定符注释。该类型与注释的全限定类名匹配。另外,为了方便起见,如果不存在名称冲突的风险,您可以使用短的类名。下面的例子演示了这两种方法:
<?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提供qualifier元数据的基于注释的替代方案。具体地说,请参见提供带有注释的限定元数据。
在某些情况下,使用不带值的注释就足够了。当注释服务于更通用的目的,并且可以跨几种不同类型的依赖项应用时,这可能很有用。例如,您可以提供一个离线目录,以便在没有Internet连接时进行搜索。首先,定义简单注释,如下例所示:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {
}
然后将注解添加到要自动连接的字段或属性,如下例所示:
public class MovieRecommender {
@Autowired
@Offline (1)
private MovieCatalog offlineCatalog;
// ...
}
现在bean定义只需要一个qualifier类型,如下例所示:
<bean class="example.SimpleMovieCatalog">
<qualifier type="Offline"/> (1)
<!-- 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定义应该包含匹配的qualifier值。这个示例还演示了可以使用bean元属性而不是<qualifier/>元素。如果可用,<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.4. 使用泛型作为自动装配限定符
除了@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实例和数组。下面的例子自动连接一个泛型List:
// 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.5. 使用CustomAutowireConfigurer
CustomAutowireConfigurer是一个BeanFactoryPostProcessor,它允许您注册自己的自定义qualifier注解类型,即使它们没有使用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符合自动装配候选bean的条件时,“主”的确定如下:如果候选bean中有一个bean定义的主属性设置为true,则选择它。
1.9.6. @Resource注入
Spring还通过在字段或bean属性setter方法上使用JSR-250 @Resource注释(jakarta.annotation.Resource)来支持注入。这是Jakarta EE中的一种常见模式:例如,在jsf管理的bean和JAX-WS端点中。Spring对Spring管理的对象也支持这种模式。
@Resource接受name属性。默认情况下,Spring将该值解释为要注入的bean名称。换句话说,它遵循by-name语义,如下例所示:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder") (1)
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; (1)
public MovieRecommender() {
}
// ...
}
The context 上下文字段是基于已知的可解析依赖类型:ApplicationContext注入的。
1.9.7. Using @Value
@Value 通常用于注入外部化属性,@PropertySource主要用于解析properties文件
@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 { }
//`application.properties` file
catalog.name=MovieCatalog
在这种情况下,catalog参数和字段将等于MovieCatalog值。
Spring提供了一个默认的宽容性嵌入式值解析器。它将尝试解析属性值,如果无法解析,则将属性名(例如${catalog.name})作为值注入。如果你想严格控制不存在的值,你应该声明一个PropertySourcesPlaceholderConfigurer bean,如下例所示:
参考文档:www.jianshu.com/p/420899939…
@Configuration
public class AppConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
当使用JavaConfig配置PropertySourcesPlaceholderConfigurer时,@Bean方法必须是静态的。
如果任何${}占位符无法解析,使用上述配置将确保Spring初始化失败。也可以使用setPlaceholderPrefix、setPlaceholderSuffix或setValueSeparator等方法来定制占位符。
Spring Boot默认配置一个PropertySourcesPlaceholderConfigurer bean,该bean将从应用程序获取属性。属性和应用程序。yml文件。
Spring提供的内置转换器支持允许自动处理简单的类型转换(例如到Integer或int)。多个逗号分隔的值可以自动转换为String数组,而不需要额外的工作。
可以提供如下默认值:
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) {
this.catalog = catalog;
}
}
Spring BeanPostProcessor在幕后使用ConversionService来处理将@Value中的String值转换为目标类型的过程。如果你想为你自己的自定义类型提供转换支持,你可以提供你自己的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;
}
}
@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.8. 使用@PostConstruct和@PreDestroy
参考文档:blog.csdn.net/weixin_3964…
CommonAnnotationBeanPostProcessor不仅可以识别@Resource注释,还可以识别JSR-250生命周期注释:jakarta.annotation.PostConstruct和jakarta.annotation.PreDestroy。在Spring 2.5中引入的对这些注释的支持提供了一种替代初始化回调和销毁回调中描述的生命周期回调机制的方法。假设CommonAnnotationBeanPostProcessor在Spring ApplicationContext中注册,那么携带这些注释之一的方法将与相应的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中被移除。截至Jakarta EE 9,该包位于雅加达。注释了。如果需要,雅加达。annotation-api工件现在需要通过Maven Central获得,只需像其他库一样添加到应用程序的类路径中即可。