本文转载自:https://www.cnblogs.com/aflyun/p/11992101.html

最近在看 SpringBoot 核编程思想(核心篇),看到走向注解驱动编程这章,里面有讲解到:在SpringFramework 5.0 引入了一个注解@Indexed ,它可以为 Spring 的模式注解添加索引,以提升应用启动性能。

在往下阅读的时候,请注意一些模式注解:

Spring注解 场景说明
@Repository 数据仓库模式注解
@Component 通用组件模式注解
@Service 服务模式注解
@Controller Web控制器模式注解
@Configuration 配置类模式注解

使用场景

在应用中使用@ComponentScan扫描 package 时,如果 package 中包含很多的类,那么 Spring 启动的时候就会变慢。

提升性能的一个方案就是提供一个 Component 的候选列表,Spring 启动时直接扫描注入这些列表就行了,而不需要一个个类去扫描,再筛选出候选 Component。

需要注意的是:在这种模式下,所有组件扫描的目标模块都必须使用这种机制——大白话将就所有的 Component 组件都必须生成到列表文件中去。

While classpath scanning is very fast, it is possible to improve the startup performance of large applications by creating a static list of candidates at compilation time. In this mode, all modules that are target of component scan must use this mechanism.

使用方式

在项目中使用的时候需要导入一个spring-context-indexer jar包。

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>xxxx</version>
<optional>true</optional>
</dependency>
</dependencies>

然后在代码中,对于使用了模式注解的类上加上@Indexed注解即可。如下:

@Indexed
@Controller
public class HelloController { }

加了上面的依赖后,项目就会自动使用索引的方式启动Spring。

原理说明

摘自官网:

简单说明一下:在项目中使用了@Indexed之后,编译打包的时候会在项目中自动生成META-INT/spring.components文件。

当Spring应用上下文执行ComponentScan扫描时,META-INT/spring.components将会被CandidateComponentsIndexLoader 读取并加载,转换为CandidateComponentsIndex对象,这样的话@ComponentScan不在扫描指定的package,而是读取CandidateComponentsIndex对象,从而达到提升性能的目的。

知道上面的原理,可以看一下org.springframework.context.index.CandidateComponentsIndexLoader的源码。

ublic final class CandidateComponentsIndexLoader {

	public static final String COMPONENTS_RESOURCE_LOCATION = "META-INF/spring.components";

    public static final String IGNORE_INDEX = "spring.index.ignore";

	private static final boolean shouldIgnoreIndex = SpringProperties.getFlag(IGNORE_INDEX);

	private static final Log logger = LogFactory.getLog(CandidateComponentsIndexLoader.class);

	private static final ConcurrentMap<ClassLoader, CandidateComponentsIndex> cache =
new ConcurrentReferenceHashMap<>(); private CandidateComponentsIndexLoader() {
} @Nullable
public static CandidateComponentsIndex loadIndex(@Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = CandidateComponentsIndexLoader.class.getClassLoader();
}
return cache.computeIfAbsent(classLoaderToUse, CandidateComponentsIndexLoader::doLoadIndex);
} @Nullable
private static CandidateComponentsIndex doLoadIndex(ClassLoader classLoader) {
if (shouldIgnoreIndex) {
return null;
} try {
Enumeration<URL> urls = classLoader.getResources(COMPONENTS_RESOURCE_LOCATION);
if (!urls.hasMoreElements()) {
return null;
}
List<Properties> result = new ArrayList<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
result.add(properties);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + result.size() + "] index(es)");
}
int totalCount = result.stream().mapToInt(Properties::size).sum();
return (totalCount > 0 ? new CandidateComponentsIndex(result) : null);
}
catch (IOException ex) {
throw new IllegalStateException("Unable to load indexes from location [" +
COMPONENTS_RESOURCE_LOCATION + "]", ex);
}
} }

使用注意点

虽然这个@Indexed注解能提升性能,但是在使用的时候也需要注意下。

假设Spring应用中存在一个包含META-INT/spring.components资源的a.jar,但是 b.jar 仅存在模式注解,那么使用@ComponentScan扫描这两个JAR中的package时,b.jar 中的模式注解不会被识别,请务必注意这样的问题。

举个列子说明下,能够更好的理解。

  • DemoA项目(使用@Indexed注解

  • DemoB项目(不使用@Indexed注解)

  • SpringBootDemo项目

    在此项目中引入DemoA.jarDemoB.jar 。然后进行如下测试,测试代码如下:

配置类,扫描模式注解

@Configuration
@ComponentScan(basePackages = "org.springboot.demo")
public class SpringIndexedConfiguration {
}

测试类:

@Test
public void testIndexedAnnotation(){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringIndexedConfiguration.class); System.out.println("获取DemoA Jar中【org.springboot.demo.controller.DemoAController】");
DemoAController demoAController = context.getBean(DemoAController.class);
System.out.println("DemoAController = " + demoAController.getClass()); System.out.println("获取DemoB Jar中【org.springboot.demo.controller.DemoBController】");
DemoBController demoBController = context.getBean(DemoBController.class);
System.out.println("DemoBController = " + demoBController.getClass());
}

结果:

beanDefinitionName = demoAController
获取DemoA Jar中【org.springboot.demo.controller.DemoAController】
DemoAController = class org.springboot.demo.controller.DemoAController
获取DemoB Jar中【org.springboot.demo.controller.DemoBController】 org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springboot.demo.controller.DemoBController' available

找不到 DemoBController

通过这样一个简单的Demo,验证了上面提到的使用注意点。

对于这种情况,Spring 官网提示了配置相关属性,不再使用index方式启动。要是这样的话,我们完全可以不添加spring-context-indexer 依赖,这样整体就不会使用index模式了。

The index is enabled automatically when a META-INF/spring.components is found on the classpath. If an index is partially available for some libraries (or use cases) but could not be built for the whole application, you can fallback to a regular classpath arrangement (as though no index was present at all) by setting spring.index.ignore to true, either as a system property or in a spring.properties file at the root of the classpath.

@Indexed 注解的更多相关文章

  1. SpringFramework5.0 @Indexed注解 简单解析

    目录 使用场景 使用方法 原理说明 使用需注意点 案例说明 参考资料 纸上得来终觉浅 绝知此事要躬行 -陆游 最近在看SpringBoot核编程思想(核心篇),看到走向注解驱动编程这章,里面有讲解到: ...

  2. mongo注解详解

    1.@Entity如果你想通过Morphia把你的对象保存到Mongo中,你首先要做的是使用@Entity注解你的类:@Entity(value="comm_user_favorite_co ...

  3. 死磕Spring之IoC篇 - BeanDefinition 的解析过程(面向注解)

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...

  4. Spring Boot 自动装配(一)

    目录 目录 前言 1.起源 2.Spring 模式注解 2.1.装配方式 2.2.派生性 3.Spring @Enable 模块驱动 3.1.Spring框架中@Enable实现方式 3.2.自定义@ ...

  5. Solr7.x学习(8)-使用spring-data-solr

    1.maven配置 <dependency> <groupId>org.springframework.data</groupId> <artifactId& ...

  6. 学习Spring-Data-Jpa(六)---spring-data-commons中的repository

    1.spring-data-commons项目 spring-data-commons项目是所有spring-data项目的核心,我们来看一下该项目下的repository包中的接口和注解. 2.Re ...

  7. static关键字有何魔法?竟让Spring Boot搞出那么多静态内部类

    生命太短暂,不要去做一些根本没有人想要的东西.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习 ...

  8. SpringBoot数据访问(三) SpringBoot整合Redis

    前言 除了对关系型数据库的整合支持外,SpringBoot对非关系型数据库也提供了非常好的支持,比如,对Redis的支持. Redis(Remote Dictionary Server,即远程字典服务 ...

  9. 这样优化Spring Boot,启动速度快到飞起!

    微服务用到一时爽,没用好就呵呵啦,特别是对于服务拆分没有把控好业务边界.拆分粒度过大等问题,某些 Spring Boot 启动速度太慢了,可能你也会有这种体验,这里将探索一下关于 Spring Boo ...

随机推荐

  1. Cloudera Manager添加主机节点

    为了监控方便,想把研发环境中的主机节点都纳入Cloudera Manager的管理中,这样在遇到问题时可方便的查看主机的硬件资源情况. 添加主机节点有多种方式,由于我是离线工作,所以选择rpm包的方式 ...

  2. 爬虫系列 | 6、详解爬虫中BeautifulSoup4的用法

    bs4,全称BeautifulSoup 4 , 它是Python独有的一种解析方式.也就是说只有Python语言才可以通过这种方式去解析数据. BeautifulSoup 3 只支持Python2,所 ...

  3. SAP内表类型及其数据读取效率评估

    内表大概分3种: 1.标准表standard tables:如果不指定BINARY SEARCH附加选项,则默认为线性查找(linear search),既一条一条的查找. 2.排序表(sorted ...

  4. EntityFramework Core如何映射动态模型?

    前言 本文我们来探讨下映射动态模型的几种方式,相信一部分童鞋项目有这样的需求,比如每天/每小时等生成一张表,此种动态模型映射非常常见,经我摸索,这里给出每一步详细思路,希望能帮助到没有任何头绪的童鞋, ...

  5. Linux 安装分区设置分区大小

    一.Linux分区挂载点介绍 Linux分区挂载点介绍,推荐容量仅供参考不是绝对,跟各系统用途以及硬盘空间配额等因素实际调整: 分区类型 介绍 备注 /boot 启动分区 一般设置100M-200M, ...

  6. JWT令牌简介及demo

    一.访问令牌的类型 二.JWT令牌 1.什么是JWT令牌 ​ JWT是JSON Web Token的缩写,即JSON Web令牌,是一种自包含令牌. JWT的使用场景: 一种情况是webapi,类似之 ...

  7. selenium八大元素定位方法

    1.ID定位 可以根据元素的id来定位属性,id是当前整个HTML页面中唯一的,所以可以通过id属性来唯一定位一个元素,是首选的元素定位方式.(动态ID不做考虑) # 导入webdriver和By f ...

  8. 房产基于Swoole的PHP RPC框架设计

    房产基于Swoole的PHP RPC框架设计 https://mp.weixin.qq.com/s/XSrKEQ-0q4DvjOGTIwYYzg

  9. https://twistedmatrix.com/documents/current/core/howto/defer.html

    https://twistedmatrix.com/documents/current/core/howto/defer.html

  10. (Oracle)DDL及其数据泵导入导出(impdp/expdp)

    create tablespace ybp_dev datafile 'G:\app\Administrator\oradata\health\ybp_dev1.dbf' size 10m autoe ...