|
对于最新稳定版本,请使用Spring Framework 7.0.1! |
类路径扫描与托管组件
本章大多数示例使用 XML 来指定生成的配置元数据
每豆子定义在春季容器内。上一节
(基于注释的容器配置)演示了如何提供大量配置
通过源级注释进行元数据。然而,即使在这些例子中,“基础”
BEAN 定义在 XML 文件中明确定义,而注释仅驱动
依赖注入。本节描述了隐式检测
通过扫描类路径来选择候选组件。候选分量是
与Filter条件匹配,并注册相应的豆定义
那个容器。这消除了使用 XML 来进行豆子注册的需求。相反,是你
可以使用注释(例如,@Component),AspectJ类型的表达式,或者你自己的表达式
自定义筛选条件,选择哪些类的BEAN定义注册于
那个容器。
|
你可以用 Java 来定义豆子,而不是用 XML 文件。看看 |
@Component以及进一步的刻板印象注释
这@Repository注释是任何满足该角色的类的标记,或者
仓库的刻板印象(也称为数据访问对象或DAO)。用途包括
该标记的自动异常翻译功能,详见例外翻译。
斯普林进一步补充了刻板印象:@Component,@Service和@Controller.@Component是任何Spring管理组件的通用刻板印象。@Repository,@Service和@Controller是 的专门化@Component为
更具体的用例(包括持久化、服务和展示
分别是图层)。因此,你可以用@Component,但通过注释@Repository,@Service或@Controller相反,你的职业更适合用工具处理或关联
带有面貌。例如,这些刻板印象注释是理想的目标
点切。@Repository,@Service和@Controller也可以
在未来Spring Framework的版本中包含额外的语义。因此,如果你是
在使用@Component或@Service对于你的服务层,@Service是
显然是更好的选择。同样,如前所述,@Repository已经
支持作为持久化层自动异常转换的标记。
使用元注释和组合注释
Spring提供的许多注释都可以作为元注释使用。
自己的代码。元注释是一种可以应用于另一个注释的注释。
例如,@Service前面提到的注释通过元注释为@Component,如下示例所示:
-
Java
-
Kotlin
@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春季MVC的注释由以下组成@Controller和@ResponseBody.
此外,组合注释还可以选择性地重新声明从
元注释以实现自定义。这对你来说尤其有用
只想暴露元注释属性的子集。比如,Spring的@SessionScope注释 把范围名称硬编码为会期但仍然允许
对代理模式.以下列表展示了SessionScope注解:
-
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但未声明代理模式如下:
-
Java
-
Kotlin
@Service
@SessionScope
public class SessionScopedService {
// ...
}
@Service
@SessionScope
class SessionScopedService {
// ...
}
你也可以覆盖代理模式,如下示例所示:
-
Java
-
Kotlin
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
// ...
}
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
class SessionScopedUserService : UserService {
// ...
}
更多详情请参见 Spring Annotation Programming Model 维基页面。
自动检测类并注册 Bean 定义
Spring 可以自动检测刻板分类并注册对应的类别豆子定义实例应用上下文.例如,以下两个类别
符合此类自动检测的条件:
-
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
}
要自动检测这些类并注册对应的豆子,你需要添加@ComponentScan给你的@Configuration类,其中basePackages属性
是这两个类的共同父包。(或者,你可以指定一个
包含每个类父包的逗号、分号或空格分隔列表。)
-
Java
-
Kotlin
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"])
class AppConfig {
// ...
}
为了简洁起见,前面的例子本可以使用值属性
注释(即,@ComponentScan(“org.example”)). |
以下替代方案使用 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:annotation-config>.通常不需要包含<context:annotation-config>元素<上下文:组件扫描>. |
|
扫描类路径包需要对应的目录 类路径中的条目。用Ant建造JAR时,确保不要这样做 激活JAR任务中仅文件开关。另外,classpath 目录可能不适合 在某些环境中,基于安全策略进行暴露——例如,独立应用在 JDK 1.7.0_45及更高版本(需要在你的清单中设置“Trusted-Library”——详见 stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources)。 在JDK 9的模块路径(Jigsaw)中,Spring的类路径扫描通常正常。
不过,确保你的组件类已经导出到你的 |
此外,AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor当你使用该
组件扫描元素。这意味着这两个分量是自动检测的,且
线路连接——所有设备都没有以XML形式提供任何豆子配置元数据。
你可以禁用 的注册AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor通过包含annotation-config属性
其值为false. |
使用滤镜定制扫描
默认情况下,类注释为@Component,@Repository,@Service,@Controller,@Configuration,或者一个自定义注释,而该注释本身被注释为@Component是
唯一检测到的候选成分。不过,你可以修改和扩展这种行为
通过应用自定义过滤器。添加为includeFilters或排除过滤器属性
这@ComponentScan注释(或称为<上下文:include-filter />或<上下文:排除过滤器 />子元素的<上下文:组件扫描>元素
XML 配置)。每个滤波元件都需要类型和表达属性。
下表描述了过滤选项:
| Filter类型 | 表达式示例 | 描述 |
|---|---|---|
注释(默认) |
|
在目标组件中,一种在类型层级存在或元存在的注释。 |
可分配的 |
|
目标组件可以分配给(扩展或实现)的类(或接口)。 |
Aspectj |
|
一个AspectJ类型的表达式,以匹配目标组件。 |
正则表达式 |
|
一个正则表达式,用目标组件的类名匹配。 |
习惯 |
|
一个自定义实现 |
以下示例展示了忽略所有的配置@Repository附注
并改用“存根”仓库:
-
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:
<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,@Repository,@Service,@Controller,@RestController或@Configuration. |
在组件中定义豆元数据
Spring 组件还可以向容器贡献 bean 定义元数据。你可以做
这与@Bean用于定义豆元数据的注释@Configuration注释类。以下示例展示了如何实现:
-
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()方法。然而,它也贡献了一个具有工厂的Beans定义
方法指的publicInstance().这@Bean注释标识
工厂方法及其他Beans定义属性,例如限定符值
这@Qualifier注解。其他方法级注释可指定为@Scope,@Lazy,以及自定义限定符注释。
除了用于组件初始化的角色外,你还可以放置@Lazy注注在标记为@Autowired或@Inject.在此背景下,
它会导致注入一个懒分辨率代理。然而,这种代理方法
相当有限。对于复杂的懒惰交互,尤其是组合时
对于可选依赖,我们推荐ObjectProvider<MyTargetBean>相反。 |
如前所述,支持自动接线字段和方法,并附带额外内容
支持自布线@Bean方法。以下示例展示了如何实现:
-
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)
}
示例中自动接线字符串方法参数状态到以下值年龄另一个名为privateInstance.Spring 表达式语言元素
通过以下符号定义该性质的价值#{ <表情> }.为@Value注释,表达式解析器预设为在
解析表达文本。
从 Spring Framework 4.3 起,你也可以声明 为 的工厂方法参数注入点(或者说它的更具体子类:依赖描述符) 到
访问触发当前 BEAN 创建的请求注入点。
注意,这仅适用于实际创建豆子实例,不适用于
注入现有实例。因此,这一特性最为合理
原型范围的豆子。对于其他示波器,工厂方法只会检测到
触发在给定作用域内创建新 BEAN 实例的注入点
(例如,触发创建懒惰单例豆的依赖)。
在这种情况下,你可以用语义谨慎使用提供的注入点元数据。
以下示例展示了如何使用注入点:
-
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}")
}
这@Bean普通Spring组件中的方法处理方式与其不同
Spring内部的对应物@Configuration类。区别在于@Component类并未通过CGLIB增强来拦截方法和字段的调用。
CGLIB 代理是指调用方法或字段的方式@Bean方法
在@Configuration类创建了 BEAN 元数据引用,指向协作对象。
这些方法不采用常规的 Java 语义调用,而是经过
容器,以提供 Spring 的常规生命周期管理和代理功能
即使通过程序调用来指代其他豆子,也包括@Bean方法。
相比之下,调用方法或字段@Bean平原内的方法@Component类具有标准的Java语义,没有特殊的CGLIB处理或其他方法
约束适用。
|
你可以声明 静态调用 Java 语言的可见性
最后,一个类别可以容纳多个 |
命名自动检测组件
当组件作为扫描过程的一部分被自动检测时,其豆名为
由豆名生成器那个扫描者知道的策略。默认情况下,任何
春季刻板印象注释(@Component,@Repository,@Service和@Controller)包含一个名称值因此,该名称被赋予了
对应的Beans定义。
如果这样的注释没有包含任何名称值或任何其他检测到的分量
(例如通过自定义过滤器发现的),默认的豆名生成器返回
未大写的非限定类别名称。例如,如果以下分量
类别检测到,名称会是myMovieLister和movieFinderImpl:
-
Java
-
Kotlin
@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}
@Service("myMovieLister")
class SimpleMovieLister {
// ...
}
-
Java
-
Kotlin
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
@Repository
class MovieFinderImpl : MovieFinder {
// ...
}
如果你不想依赖默认的豆子命名策略,可以提供自定义方式
命名豆子的策略。首先,实现豆名生成器界面,并确保包含默认的无 arg 构造函数。然后,提供完整的
在配置扫描仪时,作为以下示例注释,经过限定的类名
以及豆子定义秀。
如果你遇到命名冲突,因为多个自动检测组件分别具有
相同的非限定类别名称(即名称相同但位于
不同的包),你可能需要配置一个豆名生成器这默认为
生成豆子名称的完全限定类别名称。截至 Spring Framework 5.2.3,完全限定注释豆名生成器位于包装中org.springframework.context.annotation可用于此类目的。 |
-
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>
一般来说,在其他情况下,考虑用注释指定名称 组件可能对它进行明确引用。另一方面, 只要容器负责布线,自动生成的名称就足够了。
为自动检测组件提供示波器
与 Spring 管理组件一般一样,默认且最常见的作用域
自动检测的分量是单身 人士.不过,有时候你需要换个瞄准镜
可以由@Scope注解。你可以提供
注释中的作用域,如下示例所示:
-
Java
-
Kotlin
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
@Scope("prototype")
@Repository
class MovieFinderImpl : MovieFinder {
// ...
}
@Scope注释只在具体Beans(对于注释类)进行内省分析
组件)或工厂方法(用于@Bean方法)。与XML豆相比
定义中,没有豆子定义继承和继承的概念
类级层级对元数据而言无关紧要。 |
关于 Spring 语境中“请求”或“会话”等网页特定范围的详细信息,
参见请求、会话、应用和WebSocket的范围。与这些示波器的预建注释一样,
你也可以使用Spring的元注释来撰写自己的范围注释
方法:例如,一个自定义注释,元注释为@Scope(“原型机”),
也可能声明自定义的范围代理模式。
提供定制的示波域分辨率策略,而非依赖
基于注释的方法,你可以实现ScopeMetadataResolver接口。一定要包含默认的无arg构造器。然后你可以提供
在配置扫描仪时,完全限定的类别名称,以下示例是两者的示例
注释和Beans定义显示: |
-
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>
使用某些非单例示波器时,可能需要生成
有示范范围的对象。这种推理在《Scoped Beans》中描述为依赖关系。
为此,组件扫描中提供了作用域代理属性
元素。三种可能的值是:不,接口和目标类.例如
以下配置可生成标准的JDK动态代理:
-
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>
提供带有注释的限定元数据
这@Qualifier注释在《带限定符的注释自动配线微调》中有所讨论。
该节中的例子展示了@Qualifier注释和
自定义限定符注释,在解析 AutoWire 时提供细致控制
候选人。因为这些例子基于XML豆的定义,限定词
候选豆定义的元数据通过以下方式提供限定 符或元子元素的豆XML中的元素。当依赖类路径扫描
组件自动检测,你可以提供带有类型级别的限定符元数据
候选人类别注释。以下三个例子说明了这一点
技术:
-
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 同类型的 以提供其限定符元数据的变体,因为 元数据是按实例提供,而非按类别提供。 |
生成候选成分索引
虽然类路径扫描速度非常快,但可以提升启动性能 通过在编译时创建候选项目的静态列表来处理大型应用。在这方面 模式中,所有成为组件扫描目标的模块必须使用该机制。
你现在的@ComponentScan或<上下文:组件扫描/>指令必须保留
未更改,以请求上下文以扫描某些包中的候选人。当应用上下文检测到这样的索引时,它会自动使用它而不是扫描
课程路径。 |
要生成索引,应为每个包含 的模块添加一个额外的依赖 组件是组件扫描指令的目标。以下示例显示 如何用Maven来实现:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>6.0.23</version>
<optional>true</optional>
</dependency>
</dependencies>
在Gradle 4.5及更早版本中,依赖应在仅编译配置,如下示例所示:
dependencies {
compileOnly "org.springframework:spring-context-indexer:6.0.23"
}
在Gradle 4.6及以后版本中,依赖应在注释处理处理器配置,如下示例所示:
dependencies {
annotationProcessor "org.springframework:spring-context-indexer:6.0.23"
}
这spring-context-indexer工件生成META-INF/spring.components归档
包含在jar文件中。
在你的IDE中使用这个模式时,spring-context-indexer一定是
注册为注释处理机以确保索引在
候选组件会更新。 |
当META-INF/spring.components文件被发现
在阶级路径上。如果某些库(或用例)部分提供索引,
但无法为整个应用构建,你可以退回到普通类路径
排列(仿佛根本不存在索引)通过设置spring.index.ignore自true,要么作为JVM系统属性,要么通过春季房产机制。 |