纸上得来终觉浅 绝知此事要躬行 —陆游
最近在看SpringBoot核编程思想(核心篇),看到走向注解驱动编程这章,里面有讲解到: 在SpringFramework5.0引入了一个注解 @Indexed
,它可以为Spring的 模式注解 添加索引,以提升应用启动性能。
在往下阅读的时候,请注意一些模式注解:
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 { } 复制代码
摘自官网:
简单说明一下:在项目中使用了@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注解
)
DemoB项目(不使用 @Indexed注解
)
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,验证了上面提到的使用注意点。