此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10spring-doc.cadn.net.cn

类路径扫描和托管组件

本章中的大多数示例都使用 XML 来指定配置元数据,这些元数据 产生每个BeanDefinition在 Spring 容器中。上一节 (基于注释的容器配置) 演示如何通过源级别提供大量配置元数据 附注。然而,即使在这些示例中,“基本”bean 定义也是显式的 在 XML 文件中定义,而注释仅驱动依赖注入。spring-doc.cadn.net.cn

本节介绍通过以下方式隐式检测候选组件的选项 扫描类路径。候选组件是与筛选器匹配的类 条件,并在容器中注册了相应的 Bean 定义。 这样就无需使用 XML 来执行 bean 注册。相反,您可以使用 注释(例如,@Component)、AspectJ 类型表达式或您自己的表达式 自定义过滤器条件,以选择哪些类注册了 Bean 定义 容器。spring-doc.cadn.net.cn

您可以使用 Java 而不是使用 XML 文件来定义 Bean。看看@Configuration,@Bean,@Import@DependsOn示例注释 如何使用这些功能。spring-doc.cadn.net.cn

@Component和进一步的刻板印象注释

@Repository注释是满足存储库(也称为数据访问对象或 DAO)角色或构造型的任何类的标记。用途中 此标记是异常的自动翻译,如异常翻译中所述。spring-doc.cadn.net.cn

Spring 提供了进一步的构造型注释:@Component,@Service@Controller.@Component是任何 Spring 管理组件的通用构造型。@Repository,@Service@Controller@Component为 更具体的用例(在持久性、服务和演示中 层)。因此,您可以使用@Component,但是,通过用@Repository,@Service@Controller相反,您的类更适合通过工具进行处理或关联 与方面。例如,这些构造型注释是理想的目标 切入点。@Repository,@Service@Controller也可能 在 Spring Framework 的未来版本中携带额外的语义。因此,如果你是 在使用@Component@Service对于您的服务层,@Service是 显然是更好的选择。同样,如前所述,@Repository已经是 支持作为持久性层中自动异常转换的标记。spring-doc.cadn.net.cn

使用元注释和组合注释

Spring 提供的许多注解都可以用作 自己的代码。元注释是可以应用于另一个注释的注释。 例如,@Service前面提到的注释是用元注释的@Component,如以下示例所示:spring-doc.cadn.net.cn

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

	// ...
}
1 @Component原因@Service以与@Component.
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Component (1)
annotation class Service {

	// ...
}
1 @Component原因@Service以与@Component.

您还可以组合元注释以创建“组合注释”。例如 这@RestController来自 Spring MVC 的注释由@Controller@ResponseBody.spring-doc.cadn.net.cn

此外,组合的注释可以选择从 meta 注释以允许自定义。当您 只想公开元注释属性的子集。例如,Spring 的@SessionScope注释将作用域名称硬编码为session但仍允许 自定义proxyMode.以下列表显示了SessionScope注解:spring-doc.cadn.net.cn

@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如下:spring-doc.cadn.net.cn

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

您还可以覆盖proxyMode,如以下示例所示:spring-doc.cadn.net.cn

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

有关更多详细信息,请参阅 Spring Annotation 编程模型 wiki 页面。spring-doc.cadn.net.cn

自动检测类和注册 Bean 定义

Spring 可以自动检测定型类并注册相应的BeanDefinition实例与ApplicationContext.例如,以下两个类 有资格进行此类自动检测:spring-doc.cadn.net.cn

@Service
public class SimpleMovieLister {

	private MovieFinder movieFinder;

	public SimpleMovieLister(MovieFinder movieFinder) {
		this.movieFinder = movieFinder;
	}
}
@Service
class SimpleMovieLister(private val movieFinder: MovieFinder)
@Repository
public class JpaMovieFinder implements MovieFinder {
	// implementation elided for clarity
}
@Repository
class JpaMovieFinder : MovieFinder {
	// implementation elided for clarity
}

要自动检测这些类并注册相应的 bean,您需要将@ComponentScan给你的@Configuration类,其中basePackages属性 是两个类的通用父包。(或者,您可以指定一个 逗号或分号或空格分隔的列表,其中包含每个类的父包。spring-doc.cadn.net.cn

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
	// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"])
class AppConfig  {
	// ...
}
为简洁起见,前面的示例可以使用value属性的 注释(即,@ComponentScan("org.example")).

以下替代方法使用 XML:spring-doc.cadn.net.cn

<?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:annotation-config>元素时使用<context:component-scan>.

扫描类路径包需要存在相应的目录 类路径中的条目。使用 Ant 构建 JAR 时,请确保不要 激活 JAR 任务的仅文件开关。此外,类路径目录可能不是 根据某些环境中的安全策略公开,例如,在 JDK 1.7.0_45 及更高版本(需要在清单中设置“Trusted-Library”——参见 stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources)。spring-doc.cadn.net.cn

在模块路径(Java Module System)上,Spring 的类路径扫描通常作为 预期。但是,请确保您的组件类导出在module-info描述 符。如果您希望 Spring 调用类的非公共成员,请将 确保它们已“打开”(也就是说,它们使用opens声明而不是exports声明module-info描述符)。spring-doc.cadn.net.cn

此外,AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor当您使用 component-scan 元素。这意味着这两个组件是自动检测的,并且 连接在一起——所有这些都没有以 XML 形式提供任何 bean 配置元数据。spring-doc.cadn.net.cn

您可以禁用AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor通过包含annotation-config属性 值为false.

使用过滤器自定义扫描

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

表 1.过滤器类型
过滤器类型 示例表达式 描述

注释(默认)spring-doc.cadn.net.cn

org.example.SomeAnnotationspring-doc.cadn.net.cn

在目标组件的类型级别存在或元存在的注释。spring-doc.cadn.net.cn

可分配的spring-doc.cadn.net.cn

org.example.SomeClassspring-doc.cadn.net.cn

目标组件可分配给(扩展或实现)的类(或接口)。spring-doc.cadn.net.cn

方面Jspring-doc.cadn.net.cn

org.example..*Service+spring-doc.cadn.net.cn

要由目标组件匹配的 AspectJ 类型表达式。spring-doc.cadn.net.cn

正则表达式spring-doc.cadn.net.cn

org\.example\.Default.*spring-doc.cadn.net.cn

要与目标组件的类名匹配的正则表达式。spring-doc.cadn.net.cn

习惯spring-doc.cadn.net.cn

org.example.MyTypeFilterspring-doc.cadn.net.cn

的自定义实现org.springframework.core.type.TypeFilter接口。spring-doc.cadn.net.cn

以下示例显示了忽略所有@Repository附注 并改用“存根”存储库:spring-doc.cadn.net.cn

@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:spring-doc.cadn.net.cn

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

在组件中定义 Bean 元数据

Spring 组件还可以向容器提供 bean 定义元数据。你可以做 这与相同的@Bean用于定义 Bean 元数据的注释@Configuration带注释的类。以下示例显示了如何执行此作:spring-doc.cadn.net.cn

@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注释标识 factory 方法和其他 bean 定义属性,例如通过 这@Qualifier注解。可以指定的其他方法级注释包括@Scope,@Lazy和自定义限定符注释。spring-doc.cadn.net.cn

除了用于组件初始化的作用外,您还可以将@Lazy注塑点上的注释标有@Autowired@Inject.在这种情况下, 它导致注入延迟分辨率代理。然而,这种代理方法 相当有限。用于复杂的懒惰交互,尤其是组合 对于可选依赖项,我们建议ObjectProvider<MyTargetBean>相反。spring-doc.cadn.net.cn

如前所述,支持自动连线字段和方法,并支持额外的支持自动连线@Bean方法。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

@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设置为age另一个名为privateInstance. Spring Expression Language 元素通过符号定义属性的值#{ <expression> }.为@Value注释,表达式解析器被预配置为在以下情况下查找 bean 名称 解析表达式文本。spring-doc.cadn.net.cn

从 Spring Framework 4.3 开始,您还可以声明类型为InjectionPoint(或其更具体的子类:DependencyDescriptor) 更改为 访问触发当前 Bean 创建的请求注入点。 请注意,这仅适用于 Bean 实例的实际创建,不适用于 注入现有实例。因此,此功能对于 原型范围的 bean。对于其他范围,工厂方法只会看到 触发在给定范围内创建新 Bean 实例的注入点 (例如,触发创建惰性单例 Bean 的依赖项)。 在这种情况下,您可以将提供的注入点元数据与语义联系在一起。 以下示例演示如何使用InjectionPoint:spring-doc.cadn.net.cn

@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}")
}

@Bean常规 Spring 组件中的方法的处理方式与其 弹簧内部的对应物@Configuration类。不同的是@Component类不会使用 CGLIB 增强来拦截方法和字段的调用。 CGLIB 代理是调用@Bean方法 在@Configurationclasses 创建对协作对象的 Bean 元数据引用。 此类方法不是使用正常的 Java 语义调用的,而是通过 container 的生命周期管理与代理,以提供 Spring 的常规生命周期管理和代理 bean,即使通过编程调用@Bean方法。 相反,在@Beanplain 中的方法@Componentclass 具有标准的 Java 语义,没有特殊的 CGLIB 处理或其他 约束适用。spring-doc.cadn.net.cn

您可以声明@Bean方法static,允许在没有 将其包含的配置类创建为实例。这使得特别 定义后处理器 Bean(例如,类型BeanFactoryPostProcessorBeanPostProcessor),因为此类 bean 在容器中早期初始化 生命周期,并且应避免在该点触发配置的其他部分。spring-doc.cadn.net.cn

对静态的调用@Bean方法永远不会被容器拦截,即使在容器内也是如此@Configuration类(如本节前面所述),由于技术 限制:CGLIB 子类化只能覆盖非静态方法。结果, 直接呼叫另一个人@Bean方法具有标准的 Java 语义,结果是 在直接从工厂方法本身返回的独立实例中。spring-doc.cadn.net.cn

Java 语言的可见性@Bean方法不会对 Spring 容器中生成的 bean 定义。您可以自由声明您的 工厂方法,如您认为适合非@Configuration类,也适用于静态任何地方的方法。然而,常规@Bean方法@Configuration类需要才能被覆盖——也就是说,它们不能被声明为privatefinal.spring-doc.cadn.net.cn

@Bean方法也可以在给定组件的基类上发现,或者配置类,以及在接口中声明的 Java 8 默认方法由组件或配置类实现。这允许很多灵活地组合复杂的配置安排,甚至可以有多个从 Spring 8 开始,可以通过 Java 4.2 默认方法进行继承。spring-doc.cadn.net.cn

最后,单个类可以容纳多个@Bean相同的方法bean,作为多个工厂方法的排列,以根据可用的运行时的依赖关系。这与选择“最贪婪”的算法相同构造函数或工厂方法在其他配置场景中:具有在构造时选择最大数量的可满足依赖项,类似于容器在多个@Autowired构造 函数。spring-doc.cadn.net.cn

命名自动检测的组件

当组件在扫描过程中自动检测时,其 bean 名称为 由BeanNameGenerator该扫描仪已知的策略。spring-doc.cadn.net.cn

默认情况下,AnnotationBeanNameGenerator被使用。对于 Spring 构造型注释, 如果您通过注释的value属性,该名称将用作 相应 bean 定义中的名称。当@jakarta.inject.Named使用注释代替 Spring 构造型注释。spring-doc.cadn.net.cn

从 Spring Framework 6.1 开始,用于指定 Bean 名称不再需要value.自定义构造型注释可以 声明具有不同名称的属性(例如name) 并注释该属性 跟@AliasFor(annotation = Component.class, attribute = "value").查看源代码 声明ControllerAdvice#name()举个具体例子。spring-doc.cadn.net.cn

从 Spring Framework 6.1 开始,对基于约定的构造型名称的支持已被弃用 并将在框架的未来版本中删除。因此,自定义刻板印象 注释必须使用@AliasForvalue属性 在@Component.请参阅Repository#value()ControllerAdvice#name()具体例子。spring-doc.cadn.net.cn

如果显式 Bean 名称不能从此类注释派生,也无法从任何其他注释中派生 检测到的组件(例如自定义过滤器发现的组件),默认 Bean 名称 generator 返回不大写的非限定类名。例如,如果 检测到以下组件类,则名称为myMovieListermovieFinderImpl.spring-doc.cadn.net.cn

@Service("myMovieLister")
public class SimpleMovieLister {
	// ...
}
@Service("myMovieLister")
class SimpleMovieLister {
	// ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
	// ...
}
@Repository
class MovieFinderImpl : MovieFinder {
	// ...
}

如果您不想依赖默认的 bean 命名策略,则可以提供自定义 bean-naming 策略。首先,实现BeanNameGenerator接口,并确保包含默认的无参数构造函数。然后,提供完整的 配置扫描程序时限定的类名,如以下示例注释 和豆子定义显示。spring-doc.cadn.net.cn

如果您由于多个自动检测到的组件具有相同的非限定类名(即具有相同名称但驻留在不同包中的类)而遇到命名冲突,您可能需要配置一个BeanNameGenerator默认为生成的 bean 名称的完全限定类名称。 这FullyQualifiedAnnotationBeanNameGenerator位于包中org.springframework.context.annotation可用于此类目的。
@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>

作为一般规则,每当其他组件可能显式引用它时,请考虑使用注释指定名称。另一方面,只要容器负责布线,自动生成的名称就足够了。spring-doc.cadn.net.cn

为自动检测的组件提供范围

与一般的 Spring 托管组件一样,默认和最常见的范围autodetected 组件是singleton. 但是,有时您需要一个不同的范围这可以通过@Scope注解。 您可以在注释中提供范围的名称,如以下示例所示:spring-doc.cadn.net.cn

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
	// ...
}
@Scope("prototype")
@Repository
class MovieFinderImpl : MovieFinder {
	// ...
}
@Scope注释仅在具体 bean 类(对于带注释的组件)或工厂方法(对于@Bean方法)。与 XML bean定义相比,没有 bean 定义继承的概念,并且继承类级别的层次结构与元数据目的无关。

有关特定于 Web 的作用域(例如 Spring 上下文中的“请求”或“会话”)的详细信息,请参阅请求、会话、应用程序和 WebSocket 作用域。与这些作用域的预构建注释一样,您也可以编写自己的作用域注释:例如,自定义注释meta-annotated@Scope("prototype"),可能还声明自定义作用域代理模式。spring-doc.cadn.net.cn

要提供用于范围解析的自定义策略,而不是依赖基于注释的方法,您可以实现ScopeMetadataResolver接口。 请务必包含默认的无参数构造函数。然后,您可以在配置扫描仪时提供完全限定的类名,如以下示例注释和 bean 定义显示:
@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>

当使用某些非单例作用域时,可能需要为作用域对象生成代理。推理在作用域 Bean 作为依赖项中进行了描述。为此,组件扫描上提供了作用域代理属性 元素。 三个可能的值是:no,interfacestargetClass. 例如 以下配置将产生标准 JDK 动态代理:spring-doc.cadn.net.cn

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

提供带有注释的限定符元数据

@Qualifier注释在使用限定符微调基于注释的自动连接中进行了讨论。该部分中的示例演示了@Qualifierannotation 和 自定义限定符注释,用于在解析自动连线时提供细粒度控制 候选人。因为这些示例是基于 XML bean 定义,所以限定符 元数据是通过使用qualifiermeta子元素的bean元素。当依赖类路径扫描 自动检测组件,可以为限定符元数据提供类型级 候选类上的注释。以下三个示例演示了这一点 技术:spring-doc.cadn.net.cn

@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
	// ...
}
@Component
@Qualifier("Action")
class ActionMovieCatalog : MovieCatalog
@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
	// ...
}
@Component
@Genre("Action")
class ActionMovieCatalog : MovieCatalog {
	// ...
}
@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
	// ...
}
@Component
@Offline
class CachingMovieCatalog : MovieCatalog {
	// ...
}
与大多数基于注释的替代方案一样,请记住,注释元数据是 绑定到类定义本身,而使用 XML 允许多个 bean 提供其限定符元数据的变体,因为 元数据是按实例而不是按类提供的。