Classpath Scanning and Managed Components

本章中的大多数示例都使用 XML 来指定在 Spring 容器中产生每个 BeanDefinition 的配置元数据。前面的部分 (Annotation-based Container Configuration) 演示了如何通过源级注释提供许多配置元数据。但是,即使在那些示例中,“基础”Bean 定义仍然在 XML 文件中显式定义,而注释仅驱动依赖注入。本部分描述了一种通过扫描类路径来隐式检测候选组件的选项。候选组件是与筛选标准匹配并在容器中注册了相应 Bean 定义的类。这消除了使用 XML 来执行 Bean 注册的需要。相反,你可以使用注释(例如,@Component)、AspectJ 类型表达式或你自己的自定义筛选标准来选择向容器注册 Bean 定义的类。

Most examples in this chapter use XML to specify the configuration metadata that produces each BeanDefinition within the Spring container. The previous section (Annotation-based Container Configuration) demonstrates how to provide a lot of the configuration metadata through source-level annotations. Even in those examples, however, the "base" bean definitions are explicitly defined in the XML file, while the annotations drive only the dependency injection. This section describes an option for implicitly detecting the candidate components by scanning the classpath. Candidate components are classes that match against a filter criteria and have a corresponding bean definition registered with the container. This removes the need to use XML to perform bean registration. Instead, you can use annotations (for example, @Component), AspectJ type expressions, or your own custom filter criteria to select which classes have bean definitions registered with the container.

可以使用 Java 而不是 XML 文件来定义 bean。请查看 @Configuration@Bean@Import@DependsOn 注释,了解如何使用这些功能的示例。

You can define beans using Java rather than using XML files. Take a look at the @Configuration, @Bean, @Import, and @DependsOn annotations for examples of how to use these features.

@Component and Further Stereotype Annotations

"@49" 注解是任何实现存储库 (也称为数据访问对象或 DAO) 的角色或 "@50" 的类的标记。这个标记的用法之一是自动转换异常,如 "@51" 中所述。

The @Repository annotation is a marker for any class that fulfills the role or stereotype of a repository (also known as Data Access Object or DAO). Among the uses of this marker is the automatic translation of exceptions, as described in Exception Translation.

Spring 提供了更多陈规定型注释:@Component@Service@Controller@Component 是任何 Spring 管理组件的通用陈规定型。@Repository@Service@Controller@Component 在更具体的用例(分别在持久化、服务和表示层)中的专业化。因此,可以使用 @Component 为组件类加上注释,但通过使用 @Repository@Service@Controller 为它们加上注释,这些类更适合由工具处理或与切面关联。例如,这些陈规定型注释是切入点的理想目标。@Repository@Service@Controller 在 Spring Framework 的未来版本中也可能包含其他语义。因此,如果你在为服务层选择使用 @Component@Service 之间进行选择,@Service 明显是更好的选择。与此类似,如前所述,@Repository 已被支持为持久层中自动异常翻译的标记。

Spring provides further stereotype annotations: @Component, @Service, and @Controller. @Component is a generic stereotype for any Spring-managed component. @Repository, @Service, and @Controller are specializations of @Component for more specific use cases (in the persistence, service, and presentation layers, respectively). Therefore, you can annotate your component classes with @Component, but, by annotating them with @Repository, @Service, or @Controller instead, your classes are more properly suited for processing by tools or associating with aspects. For example, these stereotype annotations make ideal targets for pointcuts. @Repository, @Service, and @Controller may also carry additional semantics in future releases of the Spring Framework. Thus, if you are choosing between using @Component or @Service for your service layer, @Service is clearly the better choice. Similarly, as stated earlier, @Repository is already supported as a marker for automatic exception translation in your persistence layer.

Using Meta-annotations and Composed Annotations

Spring 提供的许多注解都可以作为你自己的代码中的元注解来使用。元注解是可以应用于另一个注解的注解。例如,"@52" 注解在 "@54" 中被提及,它被 "@53" 元注解,如下面的示例所示:

Many of the annotations provided by Spring can be used as meta-annotations in your own code. A meta-annotation is an annotation that can be applied to another annotation. For example, the @Service annotation mentioned earlier is meta-annotated with @Component, as the following example shows:

Java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component (1)
public @interface Service {

	// ...
}
1 The @Component causes @Service to be treated in the same way as @Component.
Kotlin
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Component (1)
annotation class Service {

	// ...
}
2 The @Component causes @Service to be treated in the same way as @Component.

还可以合并元注释以创建“复合注释”。例如,Spring MVC 中的 @RestController 注释由 @Controller@ResponseBody 组成。

You can also combine meta-annotations to create “composed annotations”. For example, the @RestController annotation from Spring MVC is composed of @Controller and @ResponseBody.

此外,复合注释可以有选择地重新声明元注释中的属性以进行自定义。在你只想公开元注释属性的子集时,这可能特别有用。例如,Spring 的 @SessionScope 注释将范围名称硬编码为 session,但仍然允许自定义 proxyMode。以下清单显示了 SessionScope 注释的定义:

In addition, composed annotations can optionally redeclare attributes from meta-annotations to allow customization. This can be particularly useful when you want to only expose a subset of the meta-annotation’s attributes. For example, Spring’s @SessionScope annotation hard codes the scope name to session but still allows customization of the proxyMode. The following listing shows the definition of the SessionScope annotation:

  • Java

  • Kotlin

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {

	/**
	 * Alias for {@link Scope#proxyMode}.
	 * <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
	 */
	@AliasFor(annotation = Scope.class)
	ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Scope(WebApplicationContext.SCOPE_SESSION)
annotation class SessionScope(
		@get:AliasFor(annotation = Scope::class)
		val proxyMode: ScopedProxyMode = ScopedProxyMode.TARGET_CLASS
)

然后,可以如下所示使用 @SessionScope 而不声明 proxyMode

You can then use @SessionScope without declaring the proxyMode as follows:

  • Java

  • Kotlin

@Service
@SessionScope
public class SessionScopedService {
	// ...
}
@Service
@SessionScope
class SessionScopedService {
	// ...
}

也可以重写 proxyMode 的值,如下例所示:

You can also override the value for the proxyMode, as the following example shows:

  • Java

  • Kotlin

@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
	// ...
}
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
class SessionScopedUserService : UserService {
	// ...
}

有关更多详细信息,请参阅 Spring 注释编程模型wiki 页面。

For further details, see the Spring Annotation Programming Model wiki page.

Automatically Detecting Classes and Registering Bean Definitions

Spring 可以自动检测陈规定型类,并将相应的 BeanDefinition 实例注册到 ApplicationContext。例如,以下两个类符合此类自动检测:

Spring can automatically detect stereotyped classes and register corresponding BeanDefinition instances with the ApplicationContext. For example, the following two classes are eligible for such autodetection:

  • Java

  • Kotlin

@Service
public class SimpleMovieLister {

	private MovieFinder movieFinder;

	public SimpleMovieLister(MovieFinder movieFinder) {
		this.movieFinder = movieFinder;
	}
}
@Service
class SimpleMovieLister(private val movieFinder: MovieFinder)
  • Java

  • Kotlin

@Repository
public class JpaMovieFinder implements MovieFinder {
	// implementation elided for clarity
}
@Repository
class JpaMovieFinder : MovieFinder {
	// implementation elided for clarity
}

要自动检测这些类并注册相应的 bean,需要将 @ComponentScan 添加到 @Configuration 类中,其中 basePackages 属性是两个类的公共父包。(或者,可以指定一个逗号分隔的、分号分隔的或空格分隔的列表,其中包含每个类的父包。)

To autodetect these classes and register the corresponding beans, you need to add @ComponentScan to your @Configuration class, where the basePackages attribute is a common parent package for the two classes. (Alternatively, you can specify a comma- or semicolon- or space-separated list that includes the parent package of each class.)

  • Java

  • Kotlin

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
	// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"])
class AppConfig  {
	// ...
}

为简洁起见,前面的示例可以使用该注释的 value 属性(即,@ComponentScan("org.example"))。

For brevity, the preceding example could have used the value attribute of the annotation (that is, @ComponentScan("org.example")).

以下替代项使用了 XML:

The following alternative uses XML:

<?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:component-scan base-package="org.example"/>

</beans>

使用 <context:component-scan> 时会隐式启用 <context:annotation-config> 的功能。在使用 <context:component-scan> 时通常不需要包含 <context:annotation-config> 元素。

The use of <context:component-scan> implicitly enables the functionality of <context:annotation-config>. There is usually no need to include the <context:annotation-config> element when using <context:component-scan>.

扫描类路径包需要类路径中存在相应的目录条目。使用 Ant 构建 JAR 时,请确保未激活 JAR 任务的仅文件开关。此外,在某些环境中,类路径目录可能无法根据安全策略公开,例如 JDK 1.7.0_45 及更高版本上的独立应用程序(这需要在清单中设置“受信任库”——请参阅 https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources)。

The scanning of classpath packages requires the presence of corresponding directory entries in the classpath. When you build JARs with Ant, make sure that you do not activate the files-only switch of the JAR task. Also, classpath directories may not be exposed based on security policies in some environments — for example, standalone apps on JDK 1.7.0_45 and higher (which requires 'Trusted-Library' setup in your manifests — see https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources).

在 JDK 9 的模块路径(拼图)上,Spring 的类路径扫描通常按预期工作。但是,请确保在 module-info 描述符中导出了组件类。如果你希望 Spring 调用类的非公共成员,请确保它们已“打开”(即,它们在 module-info 描述符中使用 opens 声明而不是 exports 声明)。

On JDK 9’s module path (Jigsaw), Spring’s classpath scanning generally works as expected. However, make sure that your component classes are exported in your module-info descriptors. If you expect Spring to invoke non-public members of your classes, make sure that they are 'opened' (that is, that they use an opens declaration instead of an exports declaration in your module-info descriptor).

此外,当你使用 component-scan 元素时,AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor 都隐含包含。这意味着这两个组件会被自动检测,并连接起来,而无需在 XML 中提供任何 bean 配置元数据。

Furthermore, the AutowiredAnnotationBeanPostProcessor and CommonAnnotationBeanPostProcessor are both implicitly included when you use the component-scan element. That means that the two components are autodetected and wired together — all without any bean configuration metadata provided in XML.

你可以通过包括 annotation-config 属性将 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor 的注册禁用,属性值设为 false

You can disable the registration of AutowiredAnnotationBeanPostProcessor and CommonAnnotationBeanPostProcessor by including the annotation-config attribute with a value of false.

Using Filters to Customize Scanning

默认情况下,只有用 @Component@Repository@Service@Controller@Configuration 或自定义注解(其中注解本身用 @Component 标记)标记的类才是唯一可以检测到的候选组件。但是,你可以通过应用自定义过滤器来修改和扩展此行为。将它们添加为 @ComponentScan 注解的 includeFiltersexcludeFilters 属性(或作为 <context:component-scan> 元素在 XML 配置中的 <context:include-filter /><context:exclude-filter /> 子元素)。每个过滤器元素都需要 typeexpression 属性。下表描述了筛选选项:

By default, classes annotated with @Component, @Repository, @Service, @Controller, @Configuration, or a custom annotation that itself is annotated with @Component are the only detected candidate components. However, you can modify and extend this behavior by applying custom filters. Add them as includeFilters or excludeFilters attributes of the @ComponentScan annotation (or as <context:include-filter /> or <context:exclude-filter /> child elements of the <context:component-scan> element in XML configuration). Each filter element requires the type and expression attributes. The following table describes the filtering options:

Table 1. Filter Types
Filter Type Example Expression Description

annotation (default)

org.example.SomeAnnotation

An annotation to be present or meta-present at the type level in target components.

assignable

org.example.SomeClass

A class (or interface) that the target components are assignable to (extend or implement).

aspectj

org.example..*Service+

An AspectJ type expression to be matched by the target components.

regex

org\.example\.Default.*

A regex expression to be matched by the target components' class names.

custom

org.example.MyTypeFilter

A custom implementation of the org.springframework.core.type.TypeFilter interface.

以下示例展示了忽略所有 @Repository 注解并改为使用 “stub” 存储库的配置:

The following example shows the configuration ignoring all @Repository annotations and using “stub” repositories instead:

  • Java

  • Kotlin

@Configuration
@ComponentScan(basePackages = "org.example",
		includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
		excludeFilters = @Filter(Repository.class))
public class AppConfig {
	// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"],
		includeFilters = [Filter(type = FilterType.REGEX, pattern = [".*Stub.*Repository"])],
		excludeFilters = [Filter(Repository::class)])
class AppConfig {
	// ...
}

以下清单展示了对应的 XML:

The following listing shows the equivalent XML:

<beans>
	<context:component-scan base-package="org.example">
		<context:include-filter type="regex"
				expression=".*Stub.*Repository"/>
		<context:exclude-filter type="annotation"
				expression="org.springframework.stereotype.Repository"/>
	</context:component-scan>
</beans>

你还可以通过在注释上设置 useDefaultFilters=false 或将 use-default-filters="false" 提供为 <component-scan/> 元素的属性来禁用默认过滤器。这有效地禁用了带 @Component@Repository@Service@Controller@RestController@Configuration 进行注释或元注释的类的自动检测。

You can also disable the default filters by setting useDefaultFilters=false on the annotation or by providing use-default-filters="false" as an attribute of the <component-scan/> element. This effectively disables automatic detection of classes annotated or meta-annotated with @Component, @Repository, @Service, @Controller, @RestController, or @Configuration.

Defining Bean Metadata within Components

Spring 组件还可以将 bean 定义元数据添加到容器。你可以使用与用于在 @Configuration 标记类中定义 bean 元数据的 @Bean 注解相同。以下示例展示了如何执行此操作:

Spring components can also contribute bean definition metadata to the container. You can do this with the same @Bean annotation used to define bean metadata within @Configuration annotated classes. The following example shows how to do so:

  • Java

  • Kotlin

@Component
public class FactoryMethodComponent {

	@Bean
	@Qualifier("public")
	public TestBean publicInstance() {
		return new TestBean("publicInstance");
	}

	public void doWork() {
		// Component method implementation omitted
	}
}
@Component
class FactoryMethodComponent {

	@Bean
	@Qualifier("public")
	fun publicInstance() = TestBean("publicInstance")

	fun doWork() {
		// Component method implementation omitted
	}
}

前面的类是一个 Spring 组件,其 doWork() 方法中有特定于应用程序的代码。但是,它还提供了一个 bean 定义,其工厂方法引用 publicInstance() 方法。@Bean 注解标识工厂方法和其他 bean 定义属性,例如通过 @Qualifier 注解进行的限定符值。可以指定的其他方法级别注解包括 @Scope@Lazy 和自定义限定符注解。

The preceding class is a Spring component that has application-specific code in its doWork() method. However, it also contributes a bean definition that has a factory method referring to the method publicInstance(). The @Bean annotation identifies the factory method and other bean definition properties, such as a qualifier value through the @Qualifier annotation. Other method-level annotations that can be specified are @Scope, @Lazy, and custom qualifier annotations.

除了组件初始化之外,你还可以将 @Lazy 注释放在带有 @Autowired@Inject 标记的注入点上。在此上下文中,它会导致懒惰解析代理的注入。但是,这种代理方法相当有限。对于复杂懒交互,特别是与可选依赖项结合使用时,我们建议使用 ObjectProvider<MyTargetBean>

In addition to its role for component initialization, you can also place the @Lazy annotation on injection points marked with @Autowired or @Inject. In this context, it leads to the injection of a lazy-resolution proxy. However, such a proxy approach is rather limited. For sophisticated lazy interactions, in particular in combination with optional dependencies, we recommend ObjectProvider<MyTargetBean> instead.

如前所述,支持自动装配的字段和方法,并额外支持对 @Bean 方法的自动装配。以下示例展示了如何执行此操作:

Autowired fields and methods are supported, as previously discussed, with additional support for autowiring of @Bean methods. The following example shows how to do so:

  • Java

  • Kotlin

@Component
public class FactoryMethodComponent {

	private static int i;

	@Bean
	@Qualifier("public")
	public TestBean publicInstance() {
		return new TestBean("publicInstance");
	}

	// use of a custom qualifier and autowiring of method parameters
	@Bean
	protected TestBean protectedInstance(
			@Qualifier("public") TestBean spouse,
			@Value("#{privateInstance.age}") String country) {
		TestBean tb = new TestBean("protectedInstance", 1);
		tb.setSpouse(spouse);
		tb.setCountry(country);
		return tb;
	}

	@Bean
	private TestBean privateInstance() {
		return new TestBean("privateInstance", i++);
	}

	@Bean
	@RequestScope
	public TestBean requestScopedInstance() {
		return new TestBean("requestScopedInstance", 3);
	}
}
@Component
class FactoryMethodComponent {

	companion object {
		private var i: Int = 0
	}

	@Bean
	@Qualifier("public")
	fun publicInstance() = TestBean("publicInstance")

	// use of a custom qualifier and autowiring of method parameters
	@Bean
	protected fun protectedInstance(
			@Qualifier("public") spouse: TestBean,
			@Value("#{privateInstance.age}") country: String) = TestBean("protectedInstance", 1).apply {
		this.spouse = spouse
		this.country = country
	}

	@Bean
	private fun privateInstance() = TestBean("privateInstance", i++)

	@Bean
	@RequestScope
	fun requestScopedInstance() = TestBean("requestScopedInstance", 3)
}

示例将 String 方法参数 country 自动装配到名为 privateInstance 的另一个 bean 的 age 属性值。Spring 表达式语言元素通过符号 #{ <expression> } 定义属性值。对于 @Value 注解,预先配置一个表达式解析器,在解析表达式文本时查找 bean 名称。

The example autowires the String method parameter country to the value of the age property on another bean named privateInstance. A Spring Expression Language element defines the value of the property through the notation #{ <expression> }. For @Value annotations, an expression resolver is preconfigured to look for bean names when resolving expression text.

从 Spring Framework 4.3 开始,你还可以声明一个类型为 InjectionPoint 的工厂方法参数(或其更具体的子类:DependencyDescriptor)以访问触发当前 bean 创建的请求注入点。请注意,这仅适用于 bean 实例的实际创建,而不适用于现有实例的注入。因此,此功能最适用于原型作用域的 bean。对于其他作用域,工厂方法只会看到触发给定作用域中新建 bean 实例的注入点(例如,触发延迟单例 bean 创建的依赖项)。在这种情况下,你可以谨慎地使用提供的注入点元数据。以下示例展示了如何使用 InjectionPoint

As of Spring Framework 4.3, you may also declare a factory method parameter of type InjectionPoint (or its more specific subclass: DependencyDescriptor) to access the requesting injection point that triggers the creation of the current bean. Note that this applies only to the actual creation of bean instances, not to the injection of existing instances. As a consequence, this feature makes most sense for beans of prototype scope. For other scopes, the factory method only ever sees the injection point that triggered the creation of a new bean instance in the given scope (for example, the dependency that triggered the creation of a lazy singleton bean). You can use the provided injection point metadata with semantic care in such scenarios. The following example shows how to use InjectionPoint:

  • Java

  • Kotlin

@Component
public class FactoryMethodComponent {

	@Bean @Scope("prototype")
	public TestBean prototypeInstance(InjectionPoint injectionPoint) {
		return new TestBean("prototypeInstance for " + injectionPoint.getMember());
	}
}
@Component
class FactoryMethodComponent {

	@Bean
	@Scope("prototype")
	fun prototypeInstance(injectionPoint: InjectionPoint) =
			TestBean("prototypeInstance for ${injectionPoint.member}")
}

常规 Spring 组件中的 @Bean 方法的处理方式与 Spring @Configuration 类中的同类方法不同。区别在于,@Component 类不会使用 CGLIB 增强,从而拦截方法和字段的调用。CGLIB 代理是 @Configuration 类中的 @Bean 方法中调用方法或字段创建对协作对象的 bean 元数据引用的方式。此类方法不会使用普通 Java 语义调用,而是通过容器提供 Spring bean 的通常生命周期管理和代理,即使通过对 @Bean 方法的编程调用引用其他 bean 也是如此。相比之下,在普通 @Component 类中的 @Bean 方法中调用方法或字段具有标准 Java 语义,不适用任何特殊 CGLIB 处理或其他约束。

The @Bean methods in a regular Spring component are processed differently than their counterparts inside a Spring @Configuration class. The difference is that @Component classes are not enhanced with CGLIB to intercept the invocation of methods and fields. CGLIB proxying is the means by which invoking methods or fields within @Bean methods in @Configuration classes creates bean metadata references to collaborating objects. Such methods are not invoked with normal Java semantics but rather go through the container in order to provide the usual lifecycle management and proxying of Spring beans, even when referring to other beans through programmatic calls to @Bean methods. In contrast, invoking a method or field in a @Bean method within a plain @Component class has standard Java semantics, with no special CGLIB processing or other constraints applying.

你可以将 @Bean 方法声明为 static,允许它们在不将其包含的配置类创建为实例的情况下被调用。在定义后期处理器 bean(例如,BeanFactoryPostProcessorBeanPostProcessor)时,这样做尤其有意义,因为此类 bean 会在容器生命周期的早期初始化,并且应该避免在此阶段触发配置的其他部分。

You may declare @Bean methods as static, allowing for them to be called without creating their containing configuration class as an instance. This makes particular sense when defining post-processor beans (for example, of type BeanFactoryPostProcessor or BeanPostProcessor), since such beans get initialized early in the container lifecycle and should avoid triggering other parts of the configuration at that point.

对 static @Bean 方法的调用永远不会被容器拦截,即使在 @Configuration 类中也是如此(如本节前面所述),这是由于技术限制:CGLIB 子类化只能覆盖非 static 方法。因此,对另一个 @Bean 方法的直接调用具有标准 Java 语义,导致从工厂方法本身直接返回一个独立的实例。

Calls to static @Bean methods never get intercepted by the container, not even within @Configuration classes (as described earlier in this section), due to technical limitations: CGLIB subclassing can override only non-static methods. As a consequence, a direct call to another @Bean method has standard Java semantics, resulting in an independent instance being returned straight from the factory method itself.

@Bean 方法的 Java 语言可见性不会对 Spring 容器中生成的 bean 定义产生直接影响。你可以在 @Configuration 类和其他任何位置自由地以你认为合适的方式声明你的工厂方法。但是,@Configuration 类中的常规 @Bean 方法需要可重写,即它们不能声明为 privatefinal

The Java language visibility of @Bean methods does not have an immediate impact on the resulting bean definition in Spring’s container. You can freely declare your factory methods as you see fit in non-@Configuration classes and also for static methods anywhere. However, regular @Bean methods in @Configuration classes need to be overridable — that is, they must not be declared as private or final.

@Bean 方法也出现在给定组件或配置类的基类以及组件或配置类实现的接口中声明的 Java 8 默认方法中。这允许在组合复杂配置方案方面有很大的灵活性,甚至可以通过 Spring 4.2 中的 Java 8 默认方法进行多重继承。

@Bean methods are also discovered on base classes of a given component or configuration class, as well as on Java 8 default methods declared in interfaces implemented by the component or configuration class. This allows for a lot of flexibility in composing complex configuration arrangements, with even multiple inheritance being possible through Java 8 default methods as of Spring 4.2.

最后,一个类可以为同一个 bean 保存多个 @Bean 方法,作为根据运行时可用的依赖项而使用的多个工厂方法的排列。这与在其他配置方案中选择“贪婪”构造函数或工厂方法的算法相同:在构造时选择具有最大数量的可满足依赖项的变体,类似于容器在多个 @Autowired 构造函数之间进行选择的方式。

Finally, a single class may hold multiple @Bean methods for the same bean, as an arrangement of multiple factory methods to use depending on available dependencies at runtime. This is the same algorithm as for choosing the “greediest” constructor or factory method in other configuration scenarios: The variant with the largest number of satisfiable dependencies is picked at construction time, analogous to how the container selects between multiple @Autowired constructors.

Naming Autodetected Components

当一个组件作为扫描过程的一部分被自动检测到时,其 bean 名称由该扫描器所知的 BeanNameGenerator 策略生成。

When a component is autodetected as part of the scanning process, its bean name is generated by the BeanNameGenerator strategy known to that scanner.

默认情况下,使用 "@55"。对于 Spring "@61",如果你通过注解的 "@56" 属性提供了一个名称,则该名称将用作相应 Bean 定义中的名称。当使用以下 JSR-250 和 JSR-330 注解代替 Spring 固定用法注解时,也会适用此约定:"@57"、"@58"、"@59" 和 "@60"。

By default, the AnnotationBeanNameGenerator is used. For Spring stereotype annotations, if you supply a name via the annotation’s value attribute that name will be used as the name in the corresponding bean definition. This convention also applies when the following JSR-250 and JSR-330 annotations are used instead of Spring stereotype annotations: @jakarta.annotation.ManagedBean, @javax.annotation.ManagedBean, @jakarta.inject.Named, and @javax.inject.Named.

从 Spring Framework 6.1 开始,不再需要用于指定 bean 名称的注解属性的名称为“value”。自定义 stereotype annotations 可以声明一个名称不同的属性(例如,“name”),并使用 @AliasFor(annotation = Component.class, attribute = "value") 注释该属性。请参阅 ControllerAdvice#name() 的源代码声明以获取具体示例。

As of Spring Framework 6.1, the name of the annotation attribute that is used to specify the bean name is no longer required to be value. Custom stereotype annotations can declare an attribute with a different name (such as name) and annotate that attribute with @AliasFor(annotation = Component.class, attribute = "value"). See the source code declaration of ControllerAdvice#name() for a concrete example.

从 Spring Framework 6.1 开始,对基于约定的 stereotype 名称的支持已弃用,并且将在框架的未来版本中移除。因此,自定义 stereotype annotations 必须使用 @AliasFor@Component 中的 value 属性声明一个显式别名。请参阅 Repository#value()ControllerAdvice#name() 的源代码声明以获取具体示例。

As of Spring Framework 6.1, support for convention-based stereotype names is deprecated and will be removed in a future version of the framework. Consequently, custom stereotype annotations must use @AliasFor to declare an explicit alias for the value attribute in @Component. See the source code declaration of Repository#value() and ControllerAdvice#name() for concrete examples.

如果无法从此类注释或任何其他检测到的组件(例如自定义筛选器发现的组件)中获取明确的 bean 名称,则默认 Bean 名称生成器将返回非限定的类小写名称。例如,如果检测到了以下组件类,名称将为 myMovieListermovieFinderImpl

If an explicit bean name cannot be derived from such an annotation or for any other detected component (such as those discovered by custom filters), the default bean name generator returns the uncapitalized non-qualified class name. For example, if the following component classes were detected, the names would be myMovieLister and movieFinderImpl.

  • Java

  • Kotlin

@Service("myMovieLister")
public class SimpleMovieLister {
	// ...
}
@Service("myMovieLister")
class SimpleMovieLister {
	// ...
}
  • Java

  • Kotlin

@Repository
public class MovieFinderImpl implements MovieFinder {
	// ...
}
@Repository
class MovieFinderImpl : MovieFinder {
	// ...
}

如果你不想依赖默认的 bean 命名策略,你可以提供一个自定义 bean 命名策略。首先,实现 BeanNameGenerator 接口,并且务必包括一个默认的无参数构造函数。然后,在配置扫描程序时提供完全限定的类名称,如下面的示例注释和 Bean 定义所示。

If you do not want to rely on the default bean-naming strategy, you can provide a custom bean-naming strategy. First, implement the BeanNameGenerator interface, and be sure to include a default no-arg constructor. Then, provide the fully qualified class name when configuring the scanner, as the following example annotation and bean definition show.

如果你因有多个自动检测的组件具有相同的非限定类名称(即,在不同包中驻留的名称相同的类)而遇到命名冲突,则可能需要配置一个 BeanNameGenerator,该 BeanNameGenerator 默认为生成的 bean 名称的完全限定类名称。从 Spring Framework 5.2.3 开始,位于包 org.springframework.context.annotation 中的 FullyQualifiedAnnotationBeanNameGenerator 可用于此类目的。

If you run into naming conflicts due to multiple autodetected components having the same non-qualified class name (i.e., classes with identical names but residing in different packages), you may need to configure a BeanNameGenerator that defaults to the fully qualified class name for the generated bean name. As of Spring Framework 5.2.3, the FullyQualifiedAnnotationBeanNameGenerator located in package org.springframework.context.annotation can be used for such purposes.

  • Java

  • Kotlin

@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
	// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"], nameGenerator = MyNameGenerator::class)
class AppConfig {
	// ...
}
<beans>
	<context:component-scan base-package="org.example"
		name-generator="org.example.MyNameGenerator" />
</beans>

作为一个通用规则,只要有其他组件可能对它进行显式引用,就考虑用注解来指定该名称。另一方面,只要容器负责连接,自动生成的名称就足够了。

As a general rule, consider specifying the name with the annotation whenever other components may be making explicit references to it. On the other hand, the auto-generated names are adequate whenever the container is responsible for wiring.

Providing a Scope for Autodetected Components

与 Spring 管理的组件一样,自动检测组件的默认范围和最常见范围是 singleton。但是,有时需要不同的范围,可由 @Scope 注解指定。您可以按照以下示例所示提供注解中的范围名称:

As with Spring-managed components in general, the default and most common scope for autodetected components is singleton. However, sometimes you need a different scope that can be specified by the @Scope annotation. You can provide the name of the scope within the annotation, as the following example shows:

  • Java

  • Kotlin

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
	// ...
}
@Scope("prototype")
@Repository
class MovieFinderImpl : MovieFinder {
	// ...
}

@Scope 注释仅内省具体 Bean 类(对于带注释的组件)或工厂方法(对于 @Bean 方法)。与 XML Bean 定义相反,没有 bean 定义继承的概念,并且类级别的继承层次结构与元数据目的无关。

@Scope annotations are only introspected on the concrete bean class (for annotated components) or the factory method (for @Bean methods). In contrast to XML bean definitions, there is no notion of bean definition inheritance, and inheritance hierarchies at the class level are irrelevant for metadata purposes.

有关 Spring 上下文中特定于 Web 的范围(例如 "@62" 或 "@63")的详细信息,请参阅 "@65"。与用于这些范围的预置注解一样,你也可以使用 Spring 的元注解方法来组合你自己的范围注解:例如,用 "@64" 进行元注解的自定义注解,也有可能声明自定义作用域代理模式。

For details on web-specific scopes such as “request” or “session” in a Spring context, see Request, Session, Application, and WebSocket Scopes. As with the pre-built annotations for those scopes, you may also compose your own scoping annotations by using Spring’s meta-annotation approach: for example, a custom annotation meta-annotated with @Scope("prototype"), possibly also declaring a custom scoped-proxy mode.

要提供一个自定义的范围解析策略,而不是依赖于基于注释的方法,您可以实现https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/ScopeMetadataResolver.html[ScopeMetadataResolver]接口。请确保包含一个默认的无参数构造函数。然后,您可以在配置扫描器时提供完全限定的类名,如下面的注释和 bean 定义示例所示:

To provide a custom strategy for scope resolution rather than relying on the annotation-based approach, you can implement the ScopeMetadataResolver interface. Be sure to include a default no-arg constructor. Then you can provide the fully qualified class name when configuring the scanner, as the following example of both an annotation and a bean definition shows:

  • Java

  • Kotlin

@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
	// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"], scopeResolver = MyScopeResolver::class)
class AppConfig {
	// ...
}
<beans>
	<context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
</beans>

在使用某些非单例范围时,可能需要为范围对象生成代理。推理在 "@69" 中描述。为此,组件扫描元素中提供了 scoped-proxy 属性。三个可能的值是:"@66"、"@67" 和 "@68"。例如,以下配置将生成标准 JDK 动态代理:

When using certain non-singleton scopes, it may be necessary to generate proxies for the scoped objects. The reasoning is described in Scoped Beans as Dependencies. For this purpose, a scoped-proxy attribute is available on the component-scan element. The three possible values are: no, interfaces, and targetClass. For example, the following configuration results in standard JDK dynamic proxies:

  • Java

  • Kotlin

@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
	// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"], scopedProxy = ScopedProxyMode.INTERFACES)
class AppConfig {
	// ...
}
<beans>
	<context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
</beans>

Providing Qualifier Metadata with Annotations

Fine-tuning Annotation-based Autowiring with Qualifiers 中讨论了 @Qualifier 注释。该部分中的示例演示了如何使用 @Qualifier 注释和自定义限定符注释在解析自动装配候选对象时提供细粒度控制。由于这些示例基于 XML Bean 定义,因此限定符元数据是使用 XML 中 bean 元素的 qualifiermeta 子元素在候选 Bean 定义上提供的。在依靠类路径扫描自动检测组件时,可以使用候选类上的类型级注释提供限定符元数据。以下三个示例演示了此技术:

The @Qualifier annotation is discussed in Fine-tuning Annotation-based Autowiring with Qualifiers. The examples in that section demonstrate the use of the @Qualifier annotation and custom qualifier annotations to provide fine-grained control when you resolve autowire candidates. Because those examples were based on XML bean definitions, the qualifier metadata was provided on the candidate bean definitions by using the qualifier or meta child elements of the bean element in the XML. When relying upon classpath scanning for auto-detection of components, you can provide the qualifier metadata with type-level annotations on the candidate class. The following three examples demonstrate this technique:

  • Java

  • Kotlin

@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
	// ...
}
@Component
@Qualifier("Action")
class ActionMovieCatalog : MovieCatalog
  • Java

  • Kotlin

@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
	// ...
}
@Component
@Genre("Action")
class ActionMovieCatalog : MovieCatalog {
	// ...
}
  • Java

  • Kotlin

@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
	// ...
}
@Component
@Offline
class CachingMovieCatalog : MovieCatalog {
	// ...
}

与大多数基于注释的替代方案一样,请记住注释元数据绑定到类定义本身,而使用 XML 允许具有相同类型的多个 Bean 在其品质元数据中提供变体,因为该元数据是按实例提供的,而不是按类提供的。

As with most annotation-based alternatives, keep in mind that the annotation metadata is bound to the class definition itself, while the use of XML allows for multiple beans of the same type to provide variations in their qualifier metadata, because that metadata is provided per-instance rather than per-class.