紙上得來終覺淺 絕知此事要躬行 —陸游html
最近在看SpringBoot覈編程思想(核心篇),看到走向註解驅動編程這章,裏面有講解到: 在SpringFramework5.0引入了一個註解@Indexed
,它能夠爲Spring的模式註解添加索引,以提高應用啓動性能。java
在往下閱讀的時候,請注意一些模式註解:spring
Spring註解 | 場景說明 |
---|---|
@Repository | 數據倉庫模式註解 |
@Component | 通用組件模式註解 |
@Service | 服務模式註解 |
@Controller | Web控制器模式註解 |
@Configuration | 配置類模式註解 |
@[toc]編程
在應用中有大量使用@ComponentScan
掃描的package包含的類越多的時候,Spring模式註解解析耗時就越長。springboot
在項目中使用的時候須要導入一個spring-context-indexer
jar包,有Maven和Gradle 兩種導入方式,具體能夠看官網,我這裏使用maven方式,引入jar配置以下:maven
<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
的源碼。url
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);
}
}
}
複製代碼
感興趣的能夠自行查看所有源碼內容。spa
雖然這個@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,驗證了上面提到的使用注意點。
歡迎關注個人公衆號,技術路,一塊兒成長。