在閱讀spring-boot相關源碼時,常常見到spring.factories文件,里面寫了自動配置(AutoConfiguration)相關的類名,因此產生了一個疑問:“明明自動配置的類已經打上了@Configuration的注解,為什么還要寫spring.factories文件?”
這個話題需要從@SpringBootApplication注解開始說起。
查看@SpringBootApplication源碼,我們能看到繼承的以下注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { …… }
其中比較重要的是@EnableAutoConfiguration和@ComponentScan兩個注解。@ComponentScan注解的作用是掃描@SpringBootApplication所在的Application類(即spring-boot項目的入口類)所在的包(basepackage)下所有的@component注解(或拓展了@component的注解)標記的bean,並注冊到spring容器中。
看到這里也許會有個疑問,在spring-boot項目中pom文件里面添加的依賴中的bean(spring-boot項目外的bean)是如何注冊到spring-boot項目的spring容器中的呢?
這就需要討論@EnableAutoConfiguration的作用。查看@EnableAutoConfiguration源碼,
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
我們可以看到比較關鍵的代碼是@Import(AutoConfigurationImportSelector.class)
,而AutoConfigurationImportSelector.class
做了什么呢?通過其源碼可以看出關鍵的部分為,
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
其中,getAutoConfigurationEntry
方法獲取了spring-boot項目中需要自動配置的項(bean),查看其源碼發現,
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
其中最重要的部分為getCandidateConfigurations
方法,它獲取了所有可能參與到項目的候選配置bean,與之對應的,getExclusions
獲取了所有不需要加載的配置bean。進一步查看getCandidateConfigurations
方法的源碼,
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; }
這個方法的具體實現為,讀取spring-boot項目的classpath下META-INF/spring.factories的內容,這個文件常常以K/V的形式存儲數據,例如:
# Auto Configuration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.HelloAutoConfiguration,\ ……
getCandidateConfigurations方法獲取需要自動配置的類,除去上面講到的需要排除(exclude)的配置類,其他類將會注冊到spring-boot項目的spring容器中。
看到這里,想必已經了解@EnableAutoConfiguration注解的工作原理,回到最初的話題,“為什么要寫spring.factories文件?”
結合前面提出的疑問——“在spring-boot項目中pom文件里面添加的依賴中的bean是如何注冊到spring-boot項目的spring容器中的呢?”,不難得出spring.factories文件是幫助spring-boot項目包以外的bean(即在pom文件中添加依賴中的bean)注冊到spring-boot項目的spring容器的結論。由於@ComponentScan注解只能掃描spring-boot項目包內的bean並注冊到spring容器中,因此需要@EnableAutoConfiguration注解來注冊項目包外的bean。而spring.factories文件,則是用來記錄項目包外需要注冊的bean類名。
————————————————
版權聲明:本文為CSDN博主「SkyeBeFreeman」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/SkyeBeFreeman/article/details/96291283