@Indexed

Spring 提供类路径扫描,用于查找指定包下的带注解的组件。假设指定包下有 1000 个类,其中只有 500 个是 Bean,那么类路径扫描会多扫描 500 次;为了解决这个问题并提高应用启动速度,会将 500 个 Bean 保存到 META-INF/spring.components 文件中,当 ClassPathBeanDefinitionScanner 实例化时,会读取该文件中的数据。

@Indexed 注解不需要我们单独使用,因为 @Component 注解使用了它:

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

但是,还需要引入一个依赖,不然 @Indexed 注解不起效果。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-indexer</artifactId>
    <version>5.3.20</version>
</dependency>

添加依赖后,不需要执行其它操作,当我们编译项目时,Spring 会生成 META-INF/spring.components 文件。此文件中包含候选组件的索引:

com.javabyexamples.spring.core.beanindexing.indexedbased.SampleComponent1=org.springframework.stereotype.Component
com.javabyexamples.spring.core.beanindexing.indexedbased.SampleRepository1=org.springframework.stereotype.Component
com.javabyexamples.spring.core.beanindexing.javaxbased.NamedService=javax.inject.Named
...

例如,Spring 将 SampleComponent1 添加到索引中,因为它具有 @Component 注释。同样,索引包含 NamedService 类,因为它具有 @java.inject.Named 注解。当 Spring 应用程序启动时,如果它找到索引文件,它将使用此静态组件列表并跳过类路径扫描。

示例运行显示了索引使用情况:

DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Using candidate component class from index: 
com.javabyexamples.spring.core.beanindexing.custom.CustomComponent1
DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Using candidate component class from index: 
com.javabyexamples.spring.core.beanindexing.javaxbased.NamedService
...

值得注意的是:如果组件没有在索引文件中列出,那么这个组件不会被加载,会被忽略。

@Indexed 注解

首先,Spring 会查找使用 @Indexed 注解的类并将其添加到索引中;Spring 还会将 @Component 注解的类添加到索引中:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component { }

也适用于使用 @Component 进行元注解的其它注解:

@Component
public class SampleComponent1 { }
 
@Repository
public class SampleRepository1 { }
 
@Service
public class SampleService1 { }

在这里,我们有三个用 @Component、@Repository 和 @Service 注解的类;当我们编译应用程序时,生成的索引文件包含以下三个类:

com.javabyexamples.spring.core.beanindexing.indexedbased.SampleComponent1=org.springframework.stereotype.Component
com.javabyexamples.spring.core.beanindexing.indexedbased.SampleRepository1=org.springframework.stereotype.Component
com.javabyexamples.spring.core.beanindexing.indexedbased.SampleService1=org.springframework.stereotype.Component

Javax 注解

Spring 还会查找使用了 javax.* 注解的类,并将它们添加到候选组件索引中。

例如,它可以使用 @javax.persistence.Entity 查找 JPA 实体类或者使用 @javax.inject.Named 定义的组件:

@Entity
public class SampleEntity1 { }

@Named
public class NamedService { }

索引文件中的每个条目都引用相关的 javax 注解:

com.javabyexamples.spring.core.beanindexing.javaxbased.NamedService=javax.inject.Named
com.javabyexamples.spring.core.beanindexing.javaxbased.SampleEntity1=javax.persistence.Entity

值得注意的是:在 @Entity 的情况下,Spring 不会为 SampleEntity1 创建 Bean。但尽管如此,它仍然需要进行类路径扫描,从而添加到候选组件索引中。

自定义注解

接下来,我们将创建自定义注解,将类标记为候选组件。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface CustomIndexed {
}

当我们用 @CustomIndexed 注解一个类时,Spring 会将其添加到索引文件中:

@CustomIndexed
public class CustomIndexed1 {
}

生成的索引文件包含 CustomIndexed1 类:

com.javabyexamples.spring.core.beanindexing.custom.CustomIndexed1=com.javabyexamples.spring.core.beanindexing.custom.CustomIndexed

多个注解

最后,我们将看看 Spring 如何处理具有多个注解的候选组件:

@Component
@CustomIndexed
@Named
public class MultiComponent1 {
}

在这里,我们使用 @Component、@Named 和 @CustomIndexed 对 MultiComponent1 类进行了注解;当我们编译应用程序时,生成的 spring.components 文件包含:

com.javabyexamples.spring.core.beanindexing.custom.CustomComponent1=org.springframework.stereotype.Component,
com.javabyexamples.spring.core.beanindexing.custom.CustomIndexed,javax.inject.Named

一个索引可以引用多个注解。

禁用候选组件索引

我们可以通过将 spring.index.ignore 设置为 true 来回退到常规类路径扫描;我们可以将此属性定义为系统属性,也可以在类路径的 spring.properties 文件中定义此属性:

spring.index.ignore=true

参考资料

Spring Candidate Component Index

本文作者:不是很聪明

本文链接:https://www.cnblogs.com/baoboshi/p/16388147.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   不是很聪明  阅读(56)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起