SpringBoot原始碼---SpringBoot中的SPI實現方式
上一篇文章中提到SpringBoot中實現自動配置時,用到了SPI機制。不知道會不會有有心人去看看我推薦的那篇博文。本篇文章將從程式碼的層次深入解讀Springboot的SPI機制。
首先,是一個很重要的註解@EnableAutoConfiguration,它的原始碼如下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { Class<?>[] exclude() default {}; String[] excludeName() default {}; }
@EnableAutoConfiguration註解就是SpringBoot實現自動配置的關鍵了。因為註解在底層會被翻譯為介面,繼承註解本質上等同於繼承介面,所以@SpringBootApplication註解繼承了@EnableAutoConfiguration註解後,就有了@EnableAutoConfiguration註解的能力。
我們再來說說@Import註解,上篇文章提到了它的作用機制。這篇文章中又要用到它的特性了。我們來看看EnableAutoConfigurationImportSelector類的部分原始碼:
public class EnableAutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware { private ConfigurableListableBeanFactory beanFactory; private Environment environment; private ClassLoader beanClassLoader; private ResourceLoader resourceLoader; @Override public String[] selectImports(AnnotationMetadata metadata) { try { AnnotationAttributes attributes = getAttributes(metadata); List<String> configurations = getCandidateConfigurations(metadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(metadata, attributes); configurations.removeAll(exclusions); configurations = sort(configurations); recordWithConditionEvaluationReport(configurations, exclusions); return configurations.toArray(new String[configurations.size()]); } catch (IOException ex) { throw new IllegalStateException(ex); } } }
這是精簡了很大篇幅程式碼後的部分原始碼(其他部分多數都是被此方法呼叫的方法)。我們不知道它是幹嘛的,但是可以知道是誰呼叫了它,我們來看看呼叫關係:
已經很說明問題了吧?在Spring容器啟動過程中,會通過invokeBeanFactoryPostProcessors()方法執行很多BeanFactoryPostProcessors()方法來完成BeanFactory的初始化。在這個過程中,EnableAutoConfigurationImportSelector類的selectImports()方法也被呼叫了。
接下來,我們來看看selectImports()方法都做了些什麼?EnableAutoConfigurationImportSelector類匯入了一個這樣的包:
import org.springframework.core.io.support.SpringFactoriesLoader;
我們看看它的原始碼(精簡):
public abstract class SpringFactoriesLoader {
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
}
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
}
}
這個類我們有利於我們繼續往下找線索的程式碼是這一行:
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
有了這一行程式碼,我們大致就可以猜出來這個SpringFactoriesLoader類大概是幹嘛的了吧?它的兩個核心方法一個是用來尋找spring.factories檔案中的Factory名稱的,一個是用來尋找類的。我們再來看看EnableAutoConfigurationImportSelector類中是怎樣使用SpringFactoriesLoader類的,以一段程式碼如下:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
return SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
}
我們現在再來看看selectImports()方法:
public String[] selectImports(AnnotationMetadata metadata) {
try {
AnnotationAttributes attributes = getAttributes(metadata);
//從spring.factories檔案中找出所有配置好的factory的名稱
List<String> configurations = getCandidateConfigurations(metadata,
attributes);
//去重
configurations = removeDuplicates(configurations);
//分析所有需要排除掉的factory類(EnableAutoConfiguration註解中配置的)
Set<String> exclusions = getExclusions(metadata, attributes);
//移除所有需要過濾放入factory類
configurations.removeAll(exclusions);
//排序
configurations = sort(configurations);
//記錄
recordWithConditionEvaluationReport(configurations, exclusions);
//陣列形式返回
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}