1.9. 基于注释的容器配置

预计阅读时间: 46 分钟

XML 设置的替代方案是由注解驱动的配置提供,它依赖于字节码元数据来连接组件,而不是使用尖括号声明。 Instead of using XML to describe a bean wiring, the developer moves the configuration into the component class itself by using annotations on the relevant class, method, or field declaration. As mentioned in Example: The AutowiredAnnotationBeanPostProcessor, using a BeanPostProcessor in conjunction with annotations is a common means of extending the Spring IoC container. For example, Spring 2.0 introduced the possibility of enforcing required properties with the @Required annotation. Spring 2.5 made it possible to follow that same general approach to drive Spring’s dependency injection. Essentially, the @Autowired annotation provides the same capabilities as described in Autowiring Collaborators but with more fine-grained control and wider applicability. Spring 2.5 also added support for JSR-250 annotations, such as @PostConstruct and @PreDestroy. Spring 3.0 added support for JSR-330 (Dependency Injection for Java) annotations contained in the javax.inject package such as @Inject and @Named. Details about those annotations can be found in the relevant section. 而不是使用 XML 来描述 bean 的绑定,开发者通过在相关的类、方法或字段声明上使用注解,将配置移动到组件类本身。如示例所述:使用 AutowiredAnnotationBeanPostProcessor ,结合注解使用是扩展 Spring IoC 容器的常用方法。例如,Spring 2.0 引入了使用 @Required 注解强制执行所需属性的可能性。Spring 2.5 使得采用相同的一般方法驱动 Spring 的依赖注入成为可能。本质上, @Autowired 注解提供了与在自动装配协作者中描述的相同功能,但具有更细粒度的控制和更广泛的应用性。Spring 2.5 还增加了对 JSR-250 注解的支持,如 @PostConstruct@PreDestroy 。Spring 3.0 增加了对包含在 javax.inject 包中的 JSR-330(Java 依赖注入)注解的支持,如 @Inject@Named 。有关这些注解的详细信息,请参阅相关部分。

Annotation injection is performed before XML injection. Thus, the XML configuration overrides the annotations for properties wired through both approaches. 注解注入在 XML 注入之前执行。因此,XML 配置覆盖了通过这两种方法连接的属性的注解。

As always, you can register the post-processors as individual bean definitions, but they can also be implicitly registered by including the following tag in an XML-based Spring configuration (notice the inclusion of the context namespace): 始终可以注册后处理器作为单独的 bean 定义,但也可以通过在基于 XML 的 Spring 配置中包含以下标签来隐式注册(注意包含 context 命名空间):

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

The <context:annotation-config/> element implicitly registers the following post-processors: <context:annotation-config/> 元素隐式注册以下后处理器:

<context:annotation-config/> only looks for annotations on beans in the same application context in which it is defined. This means that, if you put <context:annotation-config/> in a WebApplicationContext for a DispatcherServlet, it only checks for @Autowired beans in your controllers, and not your services. See The DispatcherServlet for more information.

(#beans-required-annotation)1.9.1. @Required

The @Required annotation applies to bean property setter methods, as in the following example:

public class SimpleMovieLister { private MovieFinder movieFinder; @Required public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... }
class SimpleMovieLister { @set:Required lateinit var movieFinder: MovieFinder // ... }

This annotation indicates that the affected bean property must be populated at configuration time, through an explicit property value in a bean definition or through autowiring. The container throws an exception if the affected bean property has not been populated. This allows for eager and explicit failure, avoiding NullPointerException instances or the like later on. We still recommend that you put assertions into the bean class itself (for example, into an init method). Doing so enforces those required references and values even when you use the class outside of a container.

The @Required annotation and RequiredAnnotationBeanPostProcessor are formally deprecated as of Spring Framework 5.1, in favor of using constructor injection for required settings (or a custom implementation of InitializingBean.afterPropertiesSet() or a custom @PostConstruct method along with bean property setter methods).

(#beans-autowired-annotation)1.9.2. Using@Autowired

JSR 330’s @Inject annotation can be used in place of Spring’s @Autowired annotation in the examples included in this section. See here for more details.

You can apply the @Autowired annotation to constructors, as the following example shows:

public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ... }
class MovieRecommender @Autowired constructor( private val customerPreferenceDao: CustomerPreferenceDao)

As of Spring Framework 4.3, an @Autowired annotation on such a constructor is no longer necessary if the target bean defines only one constructor to begin with. However, if several constructors are available and there is no primary/default constructor, at least one of the constructors must be annotated with @Autowired in order to instruct the container which one to use. See the discussion on constructor resolution for details. 截至 Spring Framework 4.3 版本,如果目标 Bean 一开始就只定义了一个构造函数,那么在这样一个构造函数上的 @Autowired 注解就不再必要。然而,如果有多个构造函数可用且没有主/默认构造函数,至少必须有一个构造函数使用 @Autowired 注解来指示容器使用哪一个。有关构造函数解析的讨论,请参阅详细信息。

You can also apply the @Autowired annotation to traditional setter methods, as the following example shows: 你还可以将 @Autowired 注解应用于传统 setter 方法,如下例所示:

public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... }
class SimpleMovieLister { @set:Autowired lateinit var movieFinder: MovieFinder // ... }

You can also apply the annotation to methods with arbitrary names and multiple arguments, as the following example shows: 你还可以将注释应用于具有任意名称和多个参数的方法,如下例所示:

public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ... }
class MovieRecommender { private lateinit var movieCatalog: MovieCatalog private lateinit var customerPreferenceDao: CustomerPreferenceDao @Autowired fun prepare(movieCatalog: MovieCatalog, customerPreferenceDao: CustomerPreferenceDao) { this.movieCatalog = movieCatalog this.customerPreferenceDao = customerPreferenceDao } // ... }

You can apply @Autowired to fields as well and even mix it with constructors, as the following example shows: 你也可以将 @Autowired 应用于字段,甚至可以与构造函数混合使用,如下例所示:

public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired private MovieCatalog movieCatalog; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ... }
class MovieRecommender @Autowired constructor( private val customerPreferenceDao: CustomerPreferenceDao) { @Autowired private lateinit var movieCatalog: MovieCatalog // ... }

Make sure that your target components (for example, MovieCatalog or CustomerPreferenceDao) are consistently declared by the type that you use for your @Autowired-annotated injection points. Otherwise, injection may fail due to a "no type match found" error at runtime. 确保你的目标组件(例如, MovieCatalogCustomerPreferenceDao )通过你用于你的 @Autowired 注入点的类型一致声明。否则,由于运行时出现“未找到类型匹配”错误,注入可能会失败。

For XML-defined beans or component classes found via classpath scanning, the container usually knows the concrete type up front. However, for @Bean factory methods, you need to make sure that the declared return type is sufficiently expressive. 对于通过类路径扫描发现的 XML 定义的 bean 或组件类,容器通常事先知道具体的类型。然而,对于 @Bean 工厂方法,你需要确保声明的返回类型足够明确。 For components that implement several interfaces or for components potentially referred to by their implementation type, consider declaring the most specific return type on your factory method (at least as specific as required by the injection points referring to your bean). 对于实现多个接口或可能通过其实现类型引用的组件,请考虑在你的工厂方法中声明最具体的返回类型(至少与引用你的 bean 的注入点所需的具体程度相同)。

You can also instruct Spring to provide all beans of a particular type from the ApplicationContext by adding the @Autowired annotation to a field or method that expects an array of that type, as the following example shows: 你还可以通过在期望该类型数组字段或方法上添加 @Autowired 注解,来指示 Spring 从 ApplicationContext 提供特定类型的所有 bean,如下例所示:

public class MovieRecommender { @Autowired private MovieCatalog movieCatalogs; // ... }
class MovieRecommender { @Autowired private lateinit var movieCatalogs: Array<MovieCatalog> // ... }

The same applies for typed collections, as the following example shows: 同样适用于键入集合,以下示例显示:

public class MovieRecommender { private Set<MovieCatalog> movieCatalogs; @Autowired public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) { this.movieCatalogs = movieCatalogs; } // ... }
class MovieRecommender { @Autowired lateinit var movieCatalogs: Set<MovieCatalog> // ... }

Your target beans can implement the org.springframework.core.Ordered interface or use the @Order or standard @Priority annotation if you want items in the array or list to be sorted in a specific order. Otherwise, their order follows the registration order of the corresponding target bean definitions in the container.

You can declare the @Order annotation at the target class level and on @Bean methods, potentially for individual bean definitions (in case of multiple definitions that use the same bean class). @Order values may influence priorities at injection points, but be aware that they do not influence singleton startup order, which is an orthogonal concern determined by dependency relationships and @DependsOn declarations.

Note that the standard javax.annotation.Priority annotation is not available at the @Bean level, since it cannot be declared on methods. Its semantics can be modeled through @Order values in combination with @Primary on a single bean for each type.

Even typed Map instances can be autowired as long as the expected key type is String. The map values contain all beans of the expected type, and the keys contain the corresponding bean names, as the following example shows:

public class MovieRecommender { private Map<String, MovieCatalog> movieCatalogs; @Autowired public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) { this.movieCatalogs = movieCatalogs; } // ... }
class MovieRecommender { @Autowired lateinit var movieCatalogs: Map<String, MovieCatalog> // ... }

By default, autowiring fails when no matching candidate beans are available for a given injection point. In the case of a declared array, collection, or map, at least one matching element is expected.

The default behavior is to treat annotated methods and fields as indicating required dependencies. 默认行为是将注解的方法和字段视为指示必需的依赖项。 You can change this behavior as demonstrated in the following example, enabling the framework to skip a non-satisfiable injection point through marking it as non-required (i.e., by setting the required attribute in @Autowired to false): 你可以通过以下示例中的演示更改此行为,使框架通过将其标记为非必需(即在 @Autowired 中的 required 属性设置为 false )来跳过一个不满足的注入点:

public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired(required = false) public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... }
class SimpleMovieLister { @Autowired(required = false) var movieFinder: MovieFinder? = null // ... }

A non-required method will not be called at all if its dependency (or one of its dependencies, in case of multiple arguments) is not available. A non-required field will not get populated at all in such cases, leaving its default value in place. 非必需的方法在其依赖项(或多个参数的情况下的依赖项之一)不可用的情况下根本不会被调用。在这种情况下,非必需字段将不会被填充,其默认值将保持不变。

Injected constructor and factory method arguments are a special case since the required attribute in @Autowired has a somewhat different meaning due to Spring’s constructor resolution algorithm that may potentially deal with multiple constructors. 注入构造函数和工厂方法参数是一个特殊情况,因为由于 Spring 的构造函数解析算法可能会处理多个构造函数,所以 @Autowired 中的 required 属性具有某种不同的含义。 Constructor and factory method arguments are effectively required by default but with a few special rules in a single-constructor scenario, such as multi-element injection points (arrays, collections, maps) resolving to empty instances if no matching beans are available. 构造函数和工厂方法参数默认情况下是必需的,但在单构造函数场景下有一些特殊规则,例如多元素注入点(数组、集合、映射)在没有可匹配的 bean 时解析为空实例。 This allows for a common implementation pattern where all dependencies can be declared in a unique multi-argument constructor — for example, declared as a single public constructor without an @Autowired annotation. 这允许使用一种通用的实现模式,其中所有依赖都可以在唯一的多个参数构造函数中声明——例如,声明为单个公共构造函数,没有 @Autowired 注解。

Only one constructor of any given bean class may declare @Autowired with the required attribute set to true, indicating the constructor to autowire when used as a Spring bean. As a consequence, if the required attribute is left at its default value true, only a single constructor may be annotated with @Autowired. If multiple constructors declare the annotation, they will all have to declare required=false in order to be considered as candidates for autowiring (analogous to autowire=constructor in XML). The constructor with the greatest number of dependencies that can be satisfied by matching beans in the Spring container will be chosen. If none of the candidates can be satisfied, then a primary/default constructor (if present) will be used. 仅有一个给定 bean 类的构造函数可以声明 @Autowired ,其中 required 属性设置为 true ,表示当用作 Spring bean 时自动装配的构造函数。因此,如果 required 属性保留为其默认值 true ,则只能有一个构造函数可以标注为 @Autowired 。如果有多个构造函数声明了该注解,它们都必须声明 required=false 才能被视为自动装配的候选(类似于 XML 中的 autowire=constructor )。将选择能够通过匹配 Spring 容器中的 bean 满足最多依赖项的构造函数。如果没有候选者可以满足,则将使用主/默认构造函数(如果存在)。 Similarly, if a class declares multiple constructors but none of them is annotated with @Autowired, then a primary/default constructor (if present) will be used. If a class only declares a single constructor to begin with, it will always be used, even if not annotated. Note that an annotated constructor does not have to be public. 同样,如果一个类声明了多个构造函数,但没有一个被标注为 @Autowired ,那么(如果存在)将使用主/默认构造函数。如果一个类一开始只声明了一个构造函数,那么它将始终被使用,即使没有被标注。请注意,被标注的构造函数不必是公共的。

The required attribute of @Autowired is recommended over the deprecated @Required annotation on setter methods. Setting the required attribute to false indicates that the property is not required for autowiring purposes, and the property is ignored if it cannot be autowired. @Required, on the other hand, is stronger in that it enforces the property to be set by any means supported by the container, and if no value is defined, a corresponding exception is raised. required 属性在 @Autowired 中推荐使用,而不是已弃用的 @Required 注解在设置器方法上。将 required 属性设置为 false 表示该属性对于自动装配不是必需的,如果无法自动装配,则忽略该属性。另一方面, @Required 更为严格,它强制容器通过任何支持的方式设置属性,如果没有定义值,则会抛出相应的异常。

Alternatively, you can express the non-required nature of a particular dependency through Java 8’s java.util.Optional, as the following example shows: 或者,你可以通过 Java 8 的 java.util.Optional 来表示特定依赖的非必需性,如下例所示:

public class SimpleMovieLister { @Autowired public void setMovieFinder(Optional<MovieFinder> movieFinder) { ... } }

As of Spring Framework 5.0, you can also use a @Nullable annotation (of any kind in any package — for example, javax.annotation.Nullable from JSR-305) or just leverage Kotlin builtin null-safety support: 截至 Spring Framework 5.0,你还可以使用任何包中的 @Nullable 注解(例如,来自 JSR-305 的 javax.annotation.Nullable )或直接利用 Kotlin 内置的空安全支持:

public class SimpleMovieLister { @Autowired public void setMovieFinder(@Nullable MovieFinder movieFinder) { ... } }
class SimpleMovieLister { @Autowired var movieFinder: MovieFinder? = null // ... }

You can also use @Autowired for interfaces that are well-known resolvable dependencies: BeanFactory, ApplicationContext, Environment, ResourceLoader, ApplicationEventPublisher, and MessageSource. These interfaces and their extended interfaces, such as ConfigurableApplicationContext or ResourcePatternResolver, are automatically resolved, with no special setup necessary. The following example autowires an ApplicationContext object: 你还可以使用 @Autowired 为已知可解析依赖项的接口: BeanFactoryApplicationContextEnvironmentResourceLoaderApplicationEventPublisherMessageSource 。这些接口及其扩展接口,如 ConfigurableApplicationContextResourcePatternResolver ,将自动解析,无需特殊设置。以下示例自动连接 ApplicationContext 对象:

public class MovieRecommender { @Autowired private ApplicationContext context; public MovieRecommender() { } // ... }
class MovieRecommender { @Autowired lateinit var context: ApplicationContext // ... }

The @Autowired, @Inject, @Value, and @Resource annotations are handled by Spring BeanPostProcessor implementations. This means that you cannot apply these annotations within your own BeanPostProcessor or BeanFactoryPostProcessor types (if any). These types must be 'wired up' explicitly by using XML or a Spring @Bean method. The @Autowired , @Inject , @Value , and @Resource annotations are handled by Spring BeanPostProcessor implementations. This means that you cannot apply these annotations within your own BeanPostProcessor or BeanFactoryPostProcessor types (if any). These types must be 'wired up' explicitly by using XML or a Spring @Bean method. 这些 @Autowired@Inject@Value@Resource 注解由 Spring BeanPostProcessor 实现处理。这意味着你不能在自己的 BeanPostProcessorBeanFactoryPostProcessor 类型(如果有)中应用这些注解。这些类型必须通过使用 XML 或 Spring @Bean 方法显式“连接”起来。

(#beans-autowired-annotation-primary)1.9.3. Fine-tuning Annotation-based Autowiring with@Primary

1.9.3. 使用 @Primary 进行基于注解的自动装配微调

Because autowiring by type may lead to multiple candidates, it is often necessary to have more control over the selection process. One way to accomplish this is with Spring’s @Primary annotation. @Primary indicates that a particular bean should be given preference when multiple beans are candidates to be autowired to a single-valued dependency. If exactly one primary bean exists among the candidates, it becomes the autowired value. 因为按类型自动装配可能会导致多个候选者,通常需要更多地控制选择过程。实现这一目标的一种方法是通过 Spring 的 @Primary 注解。 @Primary 表示当多个 bean 都是自动装配到单个依赖项的候选者时,应该优先考虑特定的 bean。如果候选者中恰好存在一个主 bean,它就成为自动装配的值。

Consider the following configuration that defines firstMovieCatalog as the primary MovieCatalog: 考虑以下配置,将 firstMovieCatalog 定义为主 MovieCatalog

@Configuration public class MovieConfiguration { @Bean @Primary public MovieCatalog firstMovieCatalog() {...} @Bean public MovieCatalog secondMovieCatalog() {...} // ... }
@Configuration class MovieConfiguration { @Bean @Primary fun firstMovieCatalog(): MovieCatalog {...} @Bean fun secondMovieCatalog(): MovieCatalog {...} // ... }

With the preceding configuration, the following MovieRecommender is autowired with the firstMovieCatalog: 使用前面的配置,以下 MovieRecommender 自动装配为 firstMovieCatalog

public class MovieRecommender { @Autowired private MovieCatalog movieCatalog; // ... }
class MovieRecommender { @Autowired private lateinit var movieCatalog: MovieCatalog // ... }

The corresponding bean definitions follow: 相应的 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>

(#beans-autowired-annotation-qualifiers)1.9.4. Fine-tuning Annotation-based Autowiring with Qualifiers

1.9.4. 使用限定符微调基于注解的自动装配

@Primary is an effective way to use autowiring by type with several instances when one primary candidate can be determined. When you need more control over the selection process, you can use Spring’s @Qualifier annotation. You can associate qualifier values with specific arguments, narrowing the set of type matches so that a specific bean is chosen for each argument. In the simplest case, this can be a plain descriptive value, as shown in the following example: @Primary 是在存在一个主要候选者时,使用类型自动装配的几个实例的有效方法。当你需要更多控制选择过程时,可以使用 Spring 的 @Qualifier 注解。你可以将限定符值与特定参数关联,缩小类型匹配的范围,以便为每个参数选择特定的 bean。在最简单的情况下,这可以是一个普通的描述性值,如下面的示例所示:

public class MovieRecommender { @Autowired @Qualifier("main") private MovieCatalog movieCatalog; // ... }

1

The bean with the main qualifier value is wired with the constructor argument that is qualified with the same value. 该具有 main 限定值的功能豆与构造函数参数连接 具有相同的值。

2

The bean with the action qualifier value is wired with the constructor argument that is qualified with the same value. 该具有 action 限定值的功能豆与构造函数参数连接 具有相同的值。

class MovieRecommender { @Autowired @Qualifier("main") private lateinit var movieCatalog: MovieCatalog // ... }

You can also specify the @Qualifier annotation on individual constructor arguments or method parameters, as shown in the following example: 你还可以指定单个构造函数参数或方法参数的 @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; } // ... }

1

This line adds the @Offline annotation. 这一行添加了 @Offline 注释。

class MovieRecommender { private lateinit var movieCatalog: MovieCatalog private lateinit var customerPreferenceDao: CustomerPreferenceDao @Autowired fun prepare(@Qualifier("main") movieCatalog: MovieCatalog, customerPreferenceDao: CustomerPreferenceDao) { this.movieCatalog = movieCatalog this.customerPreferenceDao = customerPreferenceDao } // ... }

The following example shows corresponding bean definitions. 以下示例显示了相应的 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>

For a fallback match, the bean name is considered a default qualifier value. Thus, you can define the bean with an id of main instead of the nested qualifier element, leading to the same matching result. However, although you can use this convention to refer to specific beans by name, @Autowired is fundamentally about type-driven injection with optional semantic qualifiers. This means that qualifier values, even with the bean name fallback, always have narrowing semantics within the set of type matches. They do not semantically express a reference to a unique bean id. Good qualifier values are main or EMEA or persistent, expressing characteristics of a specific component that are independent from the bean id, which may be auto-generated in case of an anonymous bean definition such as the one in the preceding example. 对于后备匹配,Bean 名称被视为默认限定符值。因此,你可以使用 mainid 来定义 Bean,而不是使用嵌套的限定符元素,从而得到相同的匹配结果。然而,尽管你可以使用这种约定通过名称引用特定的 Bean, @Autowired 本质上是关于类型驱动的注入,带有可选的语义限定符。这意味着限定符值,即使在 Bean 名称后备的情况下,也总是在类型匹配集合中具有缩窄语义。它们在语义上并不表达对唯一 Bean id 的引用。好的限定符值是 mainEMEApersistent ,表示与 Bean id 无关的特定组件的特性,在匿名 Bean 定义的情况下可能会自动生成,如前例所示。

Qualifiers also apply to typed collections, as discussed earlier — for example, to Set<MovieCatalog>. In this case, all matching beans, according to the declared qualifiers, are injected as a collection. This implies that qualifiers do not have to be unique. Rather, they constitute filtering criteria. For example, you can define multiple MovieCatalog beans with the same qualifier value “action”, all of which are injected into a Set<MovieCatalog> annotated with @Qualifier("action"). 限定符也适用于已类型化的集合,如前所述——例如,对 Set<MovieCatalog> 。在这种情况下,根据声明的限定符,所有匹配的豆子都被注入为一个集合。这意味着限定符不必是唯一的。相反,它们构成了过滤标准。例如,你可以定义多个具有相同限定符值“action”的 MovieCatalog 豆子,所有这些豆子都被注入到一个带有 Set<MovieCatalog> 注解的 @Qualifier("action") 中。

Letting qualifier values select against target bean names, within the type-matching candidates, does not require a @Qualifier annotation at the injection point. 允许限定值针对目标 Bean 名称在类型匹配的候选者中进行选择,不需要在注入点使用 @Qualifier 注解。 If there is no other resolution indicator (such as a qualifier or a primary marker), for a non-unique dependency situation, Spring matches the injection point name (that is, the field name or parameter name) against the target bean names and chooses the same-named candidate, if any. 如果没有其他解析指示符(例如限定符或主要标记),在非唯一依赖情况下,Spring 会将注入点名称(即字段名称或参数名称)与目标 bean 名称进行匹配,并在有同名候选者的情况下选择该候选者。

That said, if you intend to express annotation-driven injection by name, do not primarily use @Autowired, even if it is capable of selecting by bean name among type-matching candidates. Instead, use the JSR-250 @Resource annotation, which is semantically defined to identify a specific target component by its unique name, with the declared type being irrelevant for the matching process. @Autowired has rather different semantics: After selecting candidate beans by type, the specified String qualifier value is considered within those type-selected candidates only (for example, matching an account qualifier against beans marked with the same qualifier label). 尽管如此,如果你打算通过名称表达注解驱动的注入,则不应主要使用 @Autowired ,即使它能够在类型匹配的候选者中通过 bean 名称进行选择。相反,请使用 JSR-250 @Resource 注解,该注解在语义上定义为通过其唯一名称识别特定目标组件,而声明的类型对于匹配过程无关紧要。 @Autowired 具有相当不同的语义:在通过类型选择候选者之后,仅考虑那些类型选择的候选者中的指定 String 限定符值(例如,将 account 限定符与标记有相同限定符标签的 bean 进行匹配)。

For beans that are themselves defined as a collection, Map, or array type, @Resource is a fine solution, referring to the specific collection or array bean by unique name. That said, as of 4.3, you can match collection, Map, and array types through Spring’s @Autowired type matching algorithm as well, as long as the element type information is preserved in @Bean return type signatures or collection inheritance hierarchies. In this case, you can use qualifier values to select among same-typed collections, as outlined in the previous paragraph. 对于定义为集合、 Map 或数组类型的豆类, @Resource 是一个不错的解决方案,通过唯一名称引用特定的集合或数组豆。话虽如此,从 4.3 版本开始,你也可以通过 Spring 的 @Autowired 类型匹配算法匹配集合、 Map 和数组类型,只要在 @Bean 返回类型签名或集合继承层次结构中保留了元素类型信息。在这种情况下,你可以使用限定值来选择具有相同类型的集合,如前一段所述。

As of 4.3, @Autowired also considers self references for injection (that is, references back to the bean that is currently injected). Note that self injection is a fallback. Regular dependencies on other components always have precedence. 截至 4.3 版本, @Autowired 也考虑了自我引用的注入(即,对当前注入的 bean 的引用)。请注意,自我注入是一个后备方案。对其他组件的常规依赖始终具有优先级。 In that sense, self references do not participate in regular candidate selection and are therefore in particular never primary. On the contrary, they always end up as lowest precedence. 在这个意义上,自引用不参与常规候选者选择,因此特别不是首选。相反,它们总是成为最低优先级。 In practice, you should use self references as a last resort only (for example, for calling other methods on the same instance through the bean’s transactional proxy). Consider factoring out the affected methods to a separate delegate bean in such a scenario. 在实践中,你应仅将自我引用作为最后的手段(例如,通过 bean 的事务代理调用同一实例上的其他方法)。在这种情况下,考虑将受影响的方法因式分解到一个单独的代理 bean 中。 Alternatively, you can use @Resource, which may obtain a proxy back to the current bean by its unique name. 或者,你可以使用 @Resource ,这可能通过其唯一名称获取当前 bean 的代理。

Trying to inject the results from @Bean methods on the same configuration class is effectively a self-reference scenario as well. Either lazily resolve such references in the method signature where it is actually needed (as opposed to an autowired field in the configuration class) or declare the affected @Bean methods as static, decoupling them from the containing configuration class instance and its lifecycle. Otherwise, such beans are only considered in the fallback phase, with matching beans on other configuration classes selected as primary candidates instead (if available). 尝试在同一个配置类中注入 @Bean 方法的结果实际上是一种自引用场景。要么在实际需要的地方(与配置类中的自动装配字段相反)懒加载此类引用,要么将受影响的 @Bean 方法声明为 static ,从而将它们从包含的配置类实例及其生命周期中解耦。否则,此类 bean 仅在回退阶段被考虑,其他配置类上的匹配 bean 将被选为主要候选者(如果可用)。

@Autowired applies to fields, constructors, and multi-argument methods, allowing for narrowing through qualifier annotations at the parameter level. In contrast, @Resource is supported only for fields and bean property setter methods with a single argument. As a consequence, you should stick with qualifiers if your injection target is a constructor or a multi-argument method. @Autowired 适用于字段、构造函数和多参数方法,允许通过参数级别的限定符注解进行缩小。相比之下, @Resource 仅支持字段和单参数的 bean 属性设置方法。因此,如果你的注入目标是构造函数或多参数方法,你应该坚持使用限定符。

You can create your own custom qualifier annotations. To do so, define an annotation and provide the @Qualifier annotation within your definition, as the following example shows: 你可以创建自己的自定义限定符注释。为此,定义一个注释并提供定义中的 @Qualifier 注释,如下例所示:

@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Genre { String value(); }

1

This line adds the @Offline annotation. 这一行添加了 @Offline 注释。

@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER) @Retention(AnnotationRetention.RUNTIME) @Qualifier annotation class Genre(val value: String)

Then you can provide the custom qualifier on autowired fields and parameters, as the following example shows: 然后你可以在自动装配的字段和参数上提供自定义限定符,如下例所示:

public class MovieRecommender { @Autowired @Genre("Action") private MovieCatalog actionCatalog; private MovieCatalog comedyCatalog; @Autowired public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) { this.comedyCatalog = comedyCatalog; } // ... }

1

This element specifies the qualifier. 此元素指定了限定符。

class MovieRecommender { @Autowired @Genre("Action") private lateinit var actionCatalog: MovieCatalog private lateinit var comedyCatalog: MovieCatalog @Autowired fun setComedyCatalog(@Genre("Comedy") comedyCatalog: MovieCatalog) { this.comedyCatalog = comedyCatalog } // ... }

Next, you can provide the information for the candidate bean definitions. You can add <qualifier/> tags as sub-elements of the <bean/> tag and then specify the type and value to match your custom qualifier annotations. The type is matched against the fully-qualified class name of the annotation. Alternately, as a convenience if no risk of conflicting names exists, you can use the short class name. The following example demonstrates both approaches: 接下来,你可以提供候选 Bean 定义的信息。你可以将 <qualifier/> 标签作为 <bean/> 标签的子元素,然后指定 typevalue 以匹配你的自定义限定符注解。类型将与注解的完全限定类名匹配。或者,如果没有名称冲突的风险,为了方便起见,你可以使用简短的类名。以下示例演示了这两种方法:

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

In Classpath Scanning and Managed Components, you can see an annotation-based alternative to providing the qualifier metadata in XML. Specifically, see Providing Qualifier Metadata with Annotations. 在类路径扫描和管理组件中,你可以查看一种基于注解的替代方法,用于在 XML 中提供限定符元数据。具体来说,请参阅使用注解提供限定符元数据。

In some cases, using an annotation without a value may suffice. This can be useful when the annotation serves a more generic purpose and can be applied across several different types of dependencies. 在某些情况下,使用不带值的注解可能就足够了。当注解具有更通用的目的并且可以应用于多种不同类型的依赖项时,这很有用。 For example, you may provide an offline catalog that can be searched when no Internet connection is available. First, define the simple annotation, as the following example shows: 例如,你可能提供一个离线目录,以便在没有互联网连接的情况下进行搜索。首先,定义简单的注释,如下例所示:

@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Offline { }
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER) @Retention(AnnotationRetention.RUNTIME) @Qualifier annotation class Offline

Then add the annotation to the field or property to be autowired, as shown in the following example: 然后,将注解添加到要自动装配的字段或属性上,如下例所示:

public class MovieRecommender { @Autowired @Offline (1) private MovieCatalog offlineCatalog; // ... }
class MovieRecommender { @Autowired @Offline (1) private lateinit var offlineCatalog: MovieCatalog // ... }

Now the bean definition only needs a qualifier type, as shown in the following example: 现在,Bean 定义只需要一个限定符 type ,如下例所示:

<bean class="example.SimpleMovieCatalog"> <qualifier type="Offline"/> (1) <!-- inject any dependencies required by this bean --> </bean>

You can also define custom qualifier annotations that accept named attributes in addition to or instead of the simple value attribute. If multiple attribute values are then specified on a field or parameter to be autowired, a bean definition must match all such attribute values to be considered an autowire candidate. As an example, consider the following annotation definition: 你还可以定义自定义的限定符注解,这些注解除了简单的 value 属性外,还可以接受命名属性。如果在要自动装配的字段或参数上指定了多个属性值,则必须将 bean 定义与所有这些属性值匹配,才能被视为自动装配的候选者。例如,考虑以下注解定义:

@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface MovieQualifier { String genre(); Format format(); }
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER) @Retention(AnnotationRetention.RUNTIME) @Qualifier annotation class MovieQualifier(val genre: String, val format: Format)

In this case Format is an enum, defined as follows: 在这种情况下, Format 是一个枚举,定义如下:

public enum Format { VHS, DVD, BLURAY }
enum class Format { VHS, DVD, BLURAY }

The fields to be autowired are annotated with the custom qualifier and include values for both attributes: genre and format, as the following example shows: 待自动装配的字段使用自定义限定符进行注解,并包含属性的两个值: genreformat ,如下例所示:

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; // ... }
class MovieRecommender { @Autowired @MovieQualifier(format = Format.VHS, genre = "Action") private lateinit var actionVhsCatalog: MovieCatalog @Autowired @MovieQualifier(format = Format.VHS, genre = "Comedy") private lateinit var comedyVhsCatalog: MovieCatalog @Autowired @MovieQualifier(format = Format.DVD, genre = "Action") private lateinit var actionDvdCatalog: MovieCatalog @Autowired @MovieQualifier(format = Format.BLURAY, genre = "Comedy") private lateinit var comedyBluRayCatalog: MovieCatalog // ... }

Finally, the bean definitions should contain matching qualifier values. This example also demonstrates that you can use bean meta attributes instead of the <qualifier/> elements. If available, the <qualifier/> element and its attributes take precedence, but the autowiring mechanism falls back on the values provided within the <meta/> tags if no such qualifier is present, as in the last two bean definitions in the following example: 最后,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>

(#beans-generics-as-qualifiers)1.9.5. Using Generics as Autowiring Qualifiers

1.9.5. 使用泛型作为自动装配限定符

In addition to the @Qualifier annotation, you can use Java generic types as an implicit form of qualification. For example, suppose you have the following configuration: 除了 @Qualifier 注解之外,你还可以使用 Java 泛型作为隐式限定形式。例如,假设你有以下配置:

@Configuration public class MyConfiguration { @Bean public StringStore stringStore() { return new StringStore(); } @Bean public IntegerStore integerStore() { return new IntegerStore(); } }
@Configuration class MyConfiguration { @Bean fun stringStore() = StringStore() @Bean fun integerStore() = IntegerStore() }

Assuming that the preceding beans implement a generic interface, (that is, Store<String> and Store<Integer>), you can @Autowire the Store interface and the generic is used as a qualifier, as the following example shows: 假设前面的豆子实现了一个通用接口(即 Store<String>Store<Integer> ),你可以 @AutowireStore 接口,并将通用作为限定符,如下例所示:

@Autowired private Store<String> s1; // <String> qualifier, injects the stringStore bean @Autowired private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
@Autowired private lateinit var s1: Store<String> // <String> qualifier, injects the stringStore bean @Autowired private lateinit var s2: Store<Integer> // <Integer> qualifier, injects the integerStore bean

Generic qualifiers also apply when autowiring lists, Map instances and arrays. The following example autowires a generic List: 通用限定符也适用于自动装配列表、 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;
// Inject all Store beans as long as they have an <Integer> generic // Store<String> beans will not appear in this list @Autowired private lateinit var s: List<Store<Integer>>

(#beans-custom-autowire-configurer)1.9.6. UsingCustomAutowireConfigurer 1.9.6. 使用CustomAutowireConfigurer

CustomAutowireConfigurer is a BeanFactoryPostProcessor that lets you register your own custom qualifier annotation types, even if they are not annotated with Spring’s @Qualifier annotation. The following example shows how to use 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>

The AutowireCandidateResolver determines autowire candidates by: AutowireCandidateResolver 通过以下方式确定自动装配候选者:

  • The autowire-candidate value of each bean definition 每个 bean 定义的 autowire-candidate

  • Any default-autowire-candidates patterns available on the <beans/> element the default-autowire-candidates patterns available on the <beans/> element

  • The presence of @Qualifier annotations and any custom annotations registered with the CustomAutowireConfigurer 存在 @Qualifier 注释以及使用 CustomAutowireConfigurer 注册的任何自定义注释

When multiple beans qualify as autowire candidates, the determination of a “primary” is as follows: If exactly one bean definition among the candidates has a primary attribute set to true, it is selected. 当多个豆子符合自动装配候选条件时,“主要”的确定如下:如果候选者中恰好有一个豆子定义设置了 primary 属性为 true ,则选择该定义。

(#beans-resource-annotation)1.9.7. Injection with@Resource

1.9.7. 注射 @Resource

Spring also supports injection by using the JSR-250 @Resource annotation (javax.annotation.Resource) on fields or bean property setter methods. This is a common pattern in Java EE: for example, in JSF-managed beans and JAX-WS endpoints. Spring supports this pattern for Spring-managed objects as well. Spring 还支持通过在字段或 Bean 属性 setter 方法上使用 JSR-250 @Resource 注解( javax.annotation.Resource )进行注入。这是 Java EE 中的一种常见模式:例如,在 JSF 管理的 Bean 和 JAX-WS 端点中。Spring 同样支持 Spring 管理的对象使用这种模式。

@Resource takes a name attribute. By default, Spring interprets that value as the bean name to be injected. In other words, it follows by-name semantics, as demonstrated in the following example: @Resource 接收一个 name 属性。默认情况下,Spring 将此值解释为要注入的 bean 名称。换句话说,它遵循按名称语义,如下例所示:

public class SimpleMovieLister { private MovieFinder movieFinder; @Resource(name="myMovieFinder") (1) public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }

1

This line injects a @Resource. 这一行注入了 @Resource

class SimpleMovieLister { @Resource(name="myMovieFinder") (1) private lateinit var movieFinder:MovieFinder }

1

This line injects a @Resource.

If no name is explicitly specified, the default name is derived from the field name or setter method. In case of a field, it takes the field name. In case of a setter method, it takes the bean property name. The following example is going to have the bean named movieFinder injected into its setter method: 字段未显式指定名称时,默认名称由字段名称或 setter 方法派生。对于字段,它采用字段名称。对于 setter 方法,它采用 bean 属性名称。以下示例将展示如何将名为 movieFinder 的 bean 注入到其 setter 方法中:

public class SimpleMovieLister { private MovieFinder movieFinder; @Resource public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }

1

The context field is injected based on the known resolvable dependency type: ApplicationContext. context 字段基于已知的可解析依赖类型注入: ApplicationContext .

class SimpleMovieLister { @set:Resource private lateinit var movieFinder: MovieFinder }

The name provided with the annotation is resolved as a bean name by the ApplicationContext of which the CommonAnnotationBeanPostProcessor is aware. The names can be resolved through JNDI if you configure Spring’s SimpleJndiBeanFactory explicitly. However, we recommend that you rely on the default behavior and use Spring’s JNDI lookup capabilities to preserve the level of indirection. 提供的带有注释的名称通过 ApplicationContext 解析为 bean 名称, CommonAnnotationBeanPostProcessor 知道这个名称。如果你显式配置了 Spring 的 SimpleJndiBeanFactory ,则可以通过 JNDI 解析名称。然而,我们建议你依赖默认行为,并使用 Spring 的 JNDI 查找功能以保持间接级别。

In the exclusive case of @Resource usage with no explicit name specified, and similar to @Autowired, @Resource finds a primary type match instead of a specific named bean and resolves well known resolvable dependencies: the BeanFactory, ApplicationContext, ResourceLoader, ApplicationEventPublisher, and MessageSource interfaces. 在仅有的使用 @Resource 且未指定显式名称的特例中,类似于 @Autowired@Resource 找到一个主要类型匹配而不是特定命名的 bean,并成功解析了已知的可解析依赖项: BeanFactoryApplicationContextResourceLoaderApplicationEventPublisherMessageSource 接口。

Thus, in the following example, the customerPreferenceDao field first looks for a bean named "customerPreferenceDao" and then falls back to a primary type match for the type CustomerPreferenceDao: 因此,在以下示例中, customerPreferenceDao 字段首先查找名为"customerPreferenceDao"的 bean,然后回退到类型 CustomerPreferenceDao 的主类型匹配:

public class MovieRecommender { @Resource private CustomerPreferenceDao customerPreferenceDao; @Resource private ApplicationContext context; (1) public MovieRecommender() { } // ... }

1

The context field is injected based on the known resolvable dependency type: ApplicationContext. context 字段基于已知的可解析依赖类型注入: ApplicationContext .

class MovieRecommender { @Resource private lateinit var customerPreferenceDao: CustomerPreferenceDao @Resource private lateinit var context: ApplicationContext (1) // ... }

(#beans-value-annotations)1.9.8. Using@Value 1.9.8. 使用@Value

@Value is typically used to inject externalized properties: @Value 通常用于注入外部化属性:

@Component public class MovieRecommender { private final String catalog; public MovieRecommender(@Value("${catalog.name}") String catalog) { this.catalog = catalog; } }
@Component class MovieRecommender(@Value("\${catalog.name}") private val catalog: String)

With the following configuration: 使用以下配置:

@Configuration @PropertySource("classpath:application.properties") public class AppConfig {}
@Configuration @PropertySource("classpath:application.properties") class AppConfig

And the following application.properties file: 并且以下 application.properties 文件:

catalog.name=MovieCatalog

In that case, the catalog parameter and field will be equal to the MovieCatalog value. 在这种情况下, catalog 参数和字段将与 MovieCatalog 值相等。

A default lenient embedded value resolver is provided by Spring. It will try to resolve the property value and if it cannot be resolved, the property name (for example ${catalog.name}) will be injected as the value. If you want to maintain strict control over nonexistent values, you should declare a PropertySourcesPlaceholderConfigurer bean, as the following example shows: Spring 提供了一个默认的宽容嵌入式值解析器。它将尝试解析属性值,如果无法解析,则将属性名(例如 ${catalog.name} )注入为值。如果你想对不存在的值保持严格控制,应声明一个 PropertySourcesPlaceholderConfigurer 豆芽,如下例所示:

@Configuration public class AppConfig { @Bean public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } }
@Configuration class AppConfig { @Bean fun propertyPlaceholderConfigurer() = PropertySourcesPlaceholderConfigurer() }

When configuring a PropertySourcesPlaceholderConfigurer using JavaConfig, the @Bean method must be static.

Using the above configuration ensures Spring initialization failure if any ${} placeholder could not be resolved. It is also possible to use methods like setPlaceholderPrefix, setPlaceholderSuffix, or setValueSeparator to customize placeholders.

Spring Boot configures by default a PropertySourcesPlaceholderConfigurer bean that will get properties from application.properties and application.yml files.

Built-in converter support provided by Spring allows simple type conversion (to Integer or int for example) to be automatically handled. Multiple comma-separated values can be automatically converted to String array without extra effort.

It is possible to provide a default value as following:

@Component public class MovieRecommender { private final String catalog; public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) { this.catalog = catalog; } }
@Component class MovieRecommender(@Value("\${catalog.name:defaultCatalog}") private val catalog: String)

A Spring BeanPostProcessor uses a ConversionService behind the scenes to handle the process for converting the String value in @Value to the target type. If you want to provide conversion support for your own custom type, you can provide your own ConversionService bean instance as the following example shows: 一个 Spring BeanPostProcessor 在幕后使用 ConversionService 来处理将 String 值在 @Value 转换为目标类型的流程。如果你想为自己的自定义类型提供转换支持,可以像以下示例一样提供自己的 ConversionService bean 实例:

@Configuration public class AppConfig { @Bean public ConversionService conversionService() { DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(); conversionService.addConverter(new MyCustomConverter()); return conversionService; } }
@Configuration class AppConfig { @Bean fun conversionService(): ConversionService { return DefaultFormattingConversionService().apply { addConverter(MyCustomConverter()) } } }

When @Value contains a SpEL expression the value will be dynamically computed at runtime as the following example shows: 当 @Value 包含一个 SpEL 表达式时,其值将在运行时动态计算,如下例所示:

@Component public class MovieRecommender { private final String catalog; public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) { this.catalog = catalog; } }
@Component class MovieRecommender( @Value("#{systemProperties['user.catalog'] + 'Catalog'}") private val catalog: String)

SpEL also enables the use of more complex data structures: 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; } }
@Component class MovieRecommender( @Value("#{{'Thriller': 100, 'Comedy': 300}}") private val countOfMoviesPerCatalog: Map<String, Int>)

(#beans-postconstruct-and-predestroy-annotations)1.9.9. Using@PostConstruct and@PreDestroy

1.9.9. 使用 @PostConstruct@PreDestroy

The CommonAnnotationBeanPostProcessor not only recognizes the @Resource annotation but also the JSR-250 lifecycle annotations: javax.annotation.PostConstruct and javax.annotation.PreDestroy. Introduced in Spring 2.5, the support for these annotations offers an alternative to the lifecycle callback mechanism described in initialization callbacks and destruction callbacks. Provided that the CommonAnnotationBeanPostProcessor is registered within the Spring ApplicationContext, a method carrying one of these annotations is invoked at the same point in the lifecycle as the corresponding Spring lifecycle interface method or explicitly declared callback method. 该 CommonAnnotationBeanPostProcessor 不仅识别 @Resource 注解,还识别 JSR-250 生命周期注解: javax.annotation.PostConstructjavax.annotation.PreDestroy 。Spring 2.5 中引入的这些注解支持为初始化回调和销毁回调中描述的生命周期回调机制提供了一种替代方案。只要 CommonAnnotationBeanPostProcessor 在 Spring ApplicationContext 中注册,带有这些注解之一的方法就会在生命周期中与相应的 Spring 生命周期接口方法或显式声明的回调方法在同一位置被调用。 In the following example, the cache is pre-populated upon initialization and cleared upon destruction: 在以下示例中,缓存在初始化时预先填充,并在销毁时清除:

public class CachingMovieLister { @PostConstruct public void populateMovieCache() { // populates the movie cache upon initialization... } @PreDestroy public void clearMovieCache() { // clears the movie cache upon destruction... } }
class CachingMovieLister { @PostConstruct fun populateMovieCache() { // populates the movie cache upon initialization... } @PreDestroy fun clearMovieCache() { // clears the movie cache upon destruction... } }

For details about the effects of combining various lifecycle mechanisms, see Combining Lifecycle Mechanisms. 关于组合各种生命周期机制的效果的详细信息,请参阅《组合生命周期机制》。

Like @Resource, the @PostConstruct and @PreDestroy annotation types were a part of the standard Java libraries from JDK 6 to 8. However, the entire javax.annotation package got separated from the core Java modules in JDK 9 and eventually removed in JDK 11. If needed, the javax.annotation-api artifact needs to be obtained via Maven Central now, simply to be added to the application’s classpath like any other library. 类似于 @Resource@PostConstruct@PreDestroy 注解类型曾是 JDK 6 到 8 标准 Java 库的一部分。然而,整个 javax.annotation 包在 JDK 9 中被从核心 Java 模块中分离出来,并在 JDK 11 中被最终移除。如果需要,现在需要通过 Maven Central 获取 javax.annotation-api 工件,仅为了将其添加到应用程序的类路径中,就像添加任何其他库一样。