转载

SpringFramework5.0 @Indexed注解 简单解析

纸上得来终觉浅 绝知此事要躬行 —陆游

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

  • 官网地址: Spring Framework 5.1.12.RELEASE beans-scanning-index)

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

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

@[toc]

使用场景

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

使用方法

在项目中使用的时候需要导入一个 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 {

}
复制代码

原理说明

摘自官网:

SpringFramework5.0 @Indexed注解 简单解析
简单说明一下:在项目中使用了 @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);
		}
	}
}

复制代码

感兴趣的可以自行查看全部源码内容。

使用需注意点

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

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

请务必注意这样的问题。

案例说明

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

  • DemoA项目(使用 @Indexed注解

    SpringFramework5.0 @Indexed注解 简单解析
  • DemoB项目(不使用 @Indexed注解 )

SpringFramework5.0 @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,验证了上面提到的使用注意点。

原文  https://juejin.im/post/5de9064bf265da33ab63614b
正文到此结束
Loading...