參考文章: spring.factories
先說(shuō)下原理:spring.factories是spring-boot的SPI機(jī)制,采用key=value鍵值對(duì)的形式保存族扰。springBoot拓展了java spi窒典,不僅僅支持接口倍靡,還支持注解叮盘。比如:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xxx.xxx.xxx.autocfg.xxxDataSourceConfiguration
EnableAutoConfiguration就是注解。
Spring在啟動(dòng)的時(shí)候灰殴,會(huì)在不同生命周期的地方調(diào)用SpringFactoriesLoader . loadFactoryNames敬特。然后在不同生命周期的地方,匹配出特定的接口或者注解牺陶,然后進(jìn)行處理伟阔。(不同注解或者接口,作用于不同的生命周期掰伸。)
參考:BeanDefinitionRegistryPostProcessor -> BeanFactoryPostProcessor -> InstantiationAwareBeanPostProcessor -> BeanPostProcessor
當(dāng)然我這只列出來(lái)四個(gè)spring最常用的擴(kuò)展點(diǎn)接口皱炉。其實(shí)還有其他擴(kuò)展點(diǎn)接口,但是由于不常用就不列出了狮鸭,只需要知道它們會(huì)被spring安排的明明白白合搅,只會(huì)出現(xiàn)在它們?cè)摮霈F(xiàn)的地方多搀。我們程序員能做的僅僅是實(shí)現(xiàn)這些拓展點(diǎn),并不能改變拓展點(diǎn)被執(zhí)行的位置(即不能改變拓展點(diǎn)的生命周期位置)灾部。
什么還不知道拓展點(diǎn)是啥康铭?
拓展點(diǎn)就是spring為我們開(kāi)發(fā)人員預(yù)留好的接口,或者注解等等赌髓。
比如:
1从藤、org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization,這個(gè)就是拓展點(diǎn)春弥。作用于bean實(shí)例化后呛哟,初始化前的生命周期
2叠荠、org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization匿沛。該擴(kuò)展點(diǎn),作用于bean執(zhí)行完afterPropertiesSet和init-method之后的生命周期榛鼎。
下面上源碼:
比如:在spring啟動(dòng)的時(shí)候逃呼,會(huì)調(diào)用invokeBeanFactoryPostProcessors核心方法。該方法里面就拓展了加載spring-factories的地方者娱。
具體方法在org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry里面抡笼。如下:
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
繼續(xù)追蹤到org.springframework.context.annotation.ConfigurationClassParser#parse方法,如下:
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
processDeferredImportSelectors();
}
核心就在processDeferredImportSelectors方法黄鳍。
private void processDeferredImportSelectors() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
ConfigurationClass configClass = deferredImport.getConfigurationClass();
try {
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
}
}
1推姻、DeferredImportSelector 是 ImportSelector 的一個(gè)變種。
2框沟、 ImportSelector 被設(shè)計(jì)成其實(shí)和@Import注解的類(lèi)同樣的導(dǎo)入效果藏古,但是實(shí)現(xiàn) ImportSelector的類(lèi)可以條件性地決定導(dǎo)入哪些配置。
3忍燥、DeferredImportSelector 的設(shè)計(jì)目的是在所有其他的配置類(lèi)被處理后才處理拧晕。這也正是該語(yǔ)句被放到本函數(shù)最后一行的原因。
該方法有很重要的一句代碼:
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
select出所有deferredImport類(lèi)梅垄,即延遲import類(lèi)厂捞。
進(jìn)入方法org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports中:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//重點(diǎn)看!6铀俊C夷佟!机久!
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
看下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;
}
是不是找到地方了臭墨,loadFactoryNames出現(xiàn)了。參數(shù)是:EnableAutoConfiguration吞加,classLoader裙犹。這句話(huà)的意思就是:從所有spring-factoies加載的key-value對(duì)中尽狠,找出EnableAutoConfiguration注解的所有實(shí)現(xiàn)類(lèi)。不管這個(gè)實(shí)現(xiàn)類(lèi)叶圃,是在哪個(gè)jar包下袄膏,都會(huì)被加載到。如果我們想要把自己的jar打給別人掺冠,然后又想在人家應(yīng)用啟動(dòng)的時(shí)候加載我們自己bean沉馆,那么就可以在jar包里面添加spring-factories文件,添加一個(gè)key-value:org.springframework.boot.autoconfigure.EnableAutoConfiguration=xxxx德崭。類(lèi)似文章開(kāi)始那樣斥黑。看下我們工程配置眉厨,如下:
通過(guò)這種方式锌奴,我們就可以在應(yīng)用啟動(dòng)時(shí),自動(dòng)加載二方包配置的bean憾股,無(wú)需本應(yīng)用開(kāi)發(fā)者配置任何代碼鹿蜀。如果是以前,可能需要寫(xiě)個(gè)config類(lèi)服球,自己手動(dòng)配置很多二方包的bean≤钋。現(xiàn)在只要對(duì)方把spring-factoies加入它們的jar包,然后所以依賴(lài)該包的應(yīng)用斩熊,都無(wú)需顯示配置了往枣。
總結(jié):
- spring-factories是spring給開(kāi)發(fā)人員提供的SPI
- spring在啟動(dòng)過(guò)程中的整個(gè)生命周期,會(huì)在不同位置調(diào)用不同拓展點(diǎn)粉渠。而這些拓展點(diǎn)是開(kāi)發(fā)人員實(shí)現(xiàn)的分冈,為了讓spring能夠動(dòng)態(tài)加載這些拓展點(diǎn),需要SPI能力
- 想加載二方包里面的bean渣叛,但是又不想配置丈秩。那么二方包里面的spring-factories能夠滿(mǎn)足你。它里面的自動(dòng)配置類(lèi)淳衙,就相當(dāng)于你自己工程的xxxApplication類(lèi)一樣蘑秽。