spring之@Indexed注解

@Indexed是Spring5.0引入的新特性,用于优化大量@ComponentScan扫描时的性能。它通过在编译时生成META-INF/spring.components文件,存储模式注解信息,启动时读取此文件而非实际扫描,从而加快应用启动时间。使用时需注意,只有使用了@Indexed的jar包中的模式注解才会被识别。
摘要由CSDN通过智能技术生成

目录

1、简介

2、使用场景

3、使用方法 

4、原理说明

5、使用需注意点

6、案例说明

6.1、DemoA项目(使用@Indexed注解)

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

6.3、SpringBootDemo项目


1、简介

        Spring包org.springframework.stereotype下,除了@Component@Controller@Service@Repository外,在5.0版本中新增了@Indexed注解。

        应用中使用<context:component-scan />@ComponentScan扫描的package包含的类越来越多的时候,Spring启动时模式注解解析时间就会变得越长

        @Indexed注解的引入正是为了解决这个问题,项目编译打包时,会在自动生成META-INF/spring.components文件,文件包含被@Indexed注释的类的模式解析结果。当Spring应用上下文进行组件扫描时,META-INF/spring.components会被org.springframework.context.index.CandidateComponentsIndexLoader读取并加载,转换为CandidateComponentsIndex对象,此时组件扫描会读取CandidateComponentsIndex,而不进行实际扫描,从而提高组件扫描效率,减少应用启动时间


2、使用场景

        在应用中有大量使用@ComponentScan扫描的package包含的类越多的时候,Spring模式注解解析耗时就越长。 


3、使用方法 

        在项目中使用的时候需要导入一个spring-context-indexer jar包,有Maven和Gradle 两种导入方式,具体可以看官网,我这里使用maven方式,引入jar配置如下:

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

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

@Indexed
@Controller
public class HelloController {

}

4、原理说明

摘自官网:
在这里插入图片描述

         简单说明一下:在项目中使用了@Indexed之后,编译打包的时候会在项目中自动生成META-INT/spring.components文件
当Spring应用上下文执行ComponentScan扫描时,META-INT/spring.components将会被CandidateComponentsIndexLoader 读取并加载,转换为CandidateComponentsIndex对象,这样的话@ComponentScan不在扫描指定的package,而是读取CandidateComponentsIndex对象,从而达到提升性能的目的

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


public class CandidateComponentsIndexLoader {

	/**
	 * The location to look for components.
	 * <p>Can be present in multiple JAR files.
	 */
	public static final String COMPONENTS_RESOURCE_LOCATION = "META-INF/spring.components";

	// 省略了的代码......
	
	@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();
			
			// 转换为CandidateComponentsIndex对象
			return (totalCount > 0 ? new CandidateComponentsIndex(result) : null);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Unable to load indexes from location [" +
					COMPONENTS_RESOURCE_LOCATION + "]", ex);
		}
	}
}


5、使用需注意点

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

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

请务必注意这样的问题。


6、案例说明

使用时候存在上面的注意点,还是用一个简单的demo进行一下说明,能够更好的理解

6.1、DemoA项目(使用@Indexed注解

在这里插入图片描述

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

 在这里插入图片描述

6.3、SpringBootDemo项目

在此项目中引入DemoA.jar 和 DemoB.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,验证了上面提到的使用注意点。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值