@EnableAutoConfiguration註解是Spring Boot中配置自動裝載的總開關。本文將從@EnableAutoConfiguration入手,嘗試分析Spring Boot對@AutoConfigurationImportSelector註解的處理過程。 ...
一、概述
@EnableAutoConfiguration註解是Spring Boot中配置自動裝載的總開關。本文將從@EnableAutoConfiguration入手,嘗試通過源碼分析增強對Spring Boot的理解。 所用版本:Spring Boot 2.2.0.M5 + Spring Framework 5.2.0.RC11. 功能的實現:(Spring Boot部分)
boot.autoconfigure.EnableAutoConfiguration註解 -> @Import了一個AutoConfigurationImportSelector實例 -> AutoConfigurationImportSelector類(implement ImportSelector),實現了selectImports() 方法,用來篩選被@Import的Configuration類(減去exclude等)
2. 介面的調度:(Spring部分)
context.annotation.ConfigurationClassParser類的parse() 方法 -> 調用對應不同BeanDefinition類型的parse() 方法 | -> 調用context.annotation.ConfigurationClassParser.doProcessConfigurationClass()方法處理ConfigurationClass | -> 調用processImports()方法來處理所有@Import註解 | -> 遍歷每個@Import標簽,生成被註入的ImportSelector子類的實例 | -> 對於普通ImportSelector,調用其selectImport()方法,篩掉exclude的,再嵌套processImports(),對需要被@Import的類的@Import註解進行處理 | -> 對於DefferedImportSelector,只加入deferredImportSelectors列表中 -> 對defferedImportImportSelectors調用相應handler的process()方法進行處理 -> 對DefferedImportImportSelector調用processImports()
3. 介面在框架中的位置:(其中一條路徑,由頂向下)
【Spring Boot部分】 boot.SpringApplication.main() 或 ApplicationStarter.main() boot.SpringApplication.run() boot.SpringApplication.refreshContext() boot.SpringApplication.refresh() boot.web.servlet.context.ServletWebServerApplicationContext.refresh() 【Spring部分】 context.support.AbstractApplicationContext.refresh() context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors() context.support.PostPreprocessorRegistrationDelegate.invokeBeanFactoryPostProcessors() context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory() context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions() context.annotation.ConfigurationClassParser.parse() (正是上一小結所述介面)
二、源碼細節
(SpringBoot) boot.autoconfigure.EnableAutoConfiguration註解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) // 載入selector,識別AutoConfigutaion類並import public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; // @interface的參數,以方法形式聲明 Class<?>[] exclude() default {}; String[] excludeName() default {}; }@EnableAutoConfiguration註解有兩個參數和一個屬性變數。 註入了一個AutoConfigurationImportSelector類實例,看起來應該是用於篩選AutoConfiguration的Import的。 @AutoConfigurationPackage待以後另行分析。
(SpringBoot) boot.autoconfigure.AutoConfigurationImportSelector類
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { //...... @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { // 如果AutoConfiguration沒開,返回{} if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } // 將spring-autoconfigure-metadata.properties的鍵值對配置載入到PropertiesAutoConfigurationMetadata對象中並返回 AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); // 基於各種配置計算需要import的configuration和exclusion AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } // 判斷AudoConfiguration是否開啟 protected boolean isEnabled(AnnotationMetadata metadata) { if (getClass() == AutoConfigurationImportSelector.class) { // 如果配置文件中有"spring.boot.enableautoconfiguration",返回該欄位的值;否則返回true return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true); } return true; } protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // 獲取註解的屬性值 AnnotationAttributes attributes = getAttributes(annotationMetadata); // 從META-INF/spring.factories文件中獲取EnableAutoConfiguration所對應的configurations List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 去重,List轉Set再轉List configurations = removeDuplicates(configurations); // 從註解的exclude/excludeName屬性中獲取排除項 Set<String> exclusions = getExclusions(annotationMetadata, attributes); // 對於不屬於AutoConfiguration的exclude報錯 checkExcludedClasses(configurations, exclusions); // 從configurations去除exclusions configurations.removeAll(exclusions); // 由所有AutoConfigurationImportFilter類的實例再進行一次篩選,去 configurations = filter(configurations, autoConfigurationMetadata); // 把AutoConfigurationImportEvent綁定在所有AutoConfigurationImportListener子類實例上 fireAutoConfigurationImportEvents(configurations, exclusions); // 返回(configurations, exclusions)組 return new AutoConfigurationEntry(configurations, exclusions); } // ...... }
可見selectImports()是AutoConfigurationImportSelector的核心函數,其核心功能就是獲取spring.factories中EnableAutoConfiguration所對應的Configuration類列表,由@EnableAutoConfiguration註解中的exclude/excludeName參數篩選一遍,再由AutoConfigurationImportFilter類所有實例篩選一遍,得到最終的用於Import的configuration和exclusion。 該函數是被誰調用的呢?在org.springframework.context.annotation.ConfigurationClassParser類中被processImports()調用,而processImports()函數被doProcessConfigurationClass()調用。下麵從doProcessConfigurationClass() 看起。
(Spring) context.annotation.ConfigurationClassParser.doProcessConfigurationClass()方法
其中,configClass是一個ConfigurationClass實例,記錄了bean name(返回的bean名)和meta data(配置數據);sourceClass是簡單封裝後的有註解的類,主要方便對類的註解的使用,初始值是封裝過的configClass。
doProcessConfigurationClass() 對ConfigurationClass進行了各種配置,包括process @ComponentScan, process @Bean, process @Import等等。如果該SourceClass有父類,返回父類,否則返回null。
@Nullable protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // ...... // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); // ...... // Process superclass, if any if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; }
其中,processImports()方法的第三個參數中,getImports()方法嵌套的遍歷了sourceClass的註解,搜集所有@Import註解的值,即被Import的類名集合。
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException { Set<SourceClass> imports = new LinkedHashSet<>(); Set<SourceClass> visited = new LinkedHashSet<>(); collectImports(sourceClass, imports, visited); return imports; } private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited) throws IOException { // 如果是頭一次添加 if (visited.add(sourceClass)) { for (SourceClass annotation : sourceClass.getAnnotations()) { String annName = annotation.getMetadata().getClassName(); // 當前註解的類名是否是Import if (!annName.equals(Import.class.getName())) { // 嵌套遍歷被Import的類 collectImports(annotation, imports, visited); } } // 增加Import註解的值,即被Import的類名 imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); } }
(Spring) context.annotation.ConfigurationClassParser.processImports()方法
processImports() 的第一個參數configClass,是上層函數processConfigurationClass()的唯一參數,即被處理的Configuration類。第二個參數currentSourceClass是configClass的SourceClass類封裝。第三個參數是嵌套遍歷出的所有需要被Import的類。第四個參數指定是否檢查迴圈import。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) { // ...... for (SourceClass candidate : importCandidates) { // 如果candidate(即被@Import的類)是ImportSelector的子類 if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); // 生成candidate class(一個ImportSelector子類)的實例 ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); // 將ImportSelector子類實例掛載為對應功能的Aware類(用於消息通知?) ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); // DeferredImportSelector是一種特殊的ImportSelector,這裡單獨處理 if (selector instanceof DeferredImportSelector) { // 掛到deferredImportSelectors列表上 this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { // 篩選符合要求的@Import的名字 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); // 轉換成名字對應的類的集合 Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); // 嵌套判斷被@Import的類是不是還有@Import註解 processImports(configClass, currentSourceClass, importSourceClasses, false); } } // ...... } // ...... }
(Spring) context.annotation.ConfigurationClassParser.DefferedImportSelectorHandler私有類
deferredImportSelectors已被初始化為ArrayList<>(),因此全部走else分支。
private class DeferredImportSelectorHandler { @Nullable private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>(); public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) { DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder( configClass, importSelector); // 一個封裝後的pair // deferredImportSelectors被初始化為ArrayList<>(),所以if分支永遠不會執行到? if (this.deferredImportSelectors == null) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); handler.register(holder); // 註冊到DeferredImportSelectorGroupingHandler.configurationClasses中 handler.processGroupImports(); // -> processImports() } else { // 所有的DeferredImportSelector類實例都掛到deferredImportSelectors列表上 this.deferredImportSelectors.add(holder); } } // ...... }
那麼deferredImportSelectors上掛載的DefferedImportSelector類是何時處理的呢? doProcessConfigurationClass() 方法被processConfigurationClass() 調用,而processConfigurationClass() 被parse() 調用。可以看到在處理完所有的普通ImportSelector類後,即嵌套載入需要的被Import的類的實例之後,再統一處理DefferedImportSelector類。
(Spring) context.annotation.ConfigurationClassParser.processConfigurationClass() 方法
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { // ... // Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass); do { // 如果sourceClass有父類會返回父類,否則返回null sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass); }
(Spring) context.annotation.ConfigurationClassParser.parse() 方法
public void parse(Set<BeanDefinitionHolder> configCandidates) { // 處理所有的ImportSelector類,其中DeferredImportSelector類只掛在deferredImportSelectorHandler列表上不處理,其他均處理,即嵌套遍歷被Import的類 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); } } // 處理deferredImportSelectorHandler上掛著的DeferredImportSelector類 this.deferredImportSelectorHandler.process(); } protected final void parse(@Nullable String className, String beanName) throws IOException { Assert.notNull(className, "No bean class name for configuration class bean definition"); MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className); processConfigurationClass(new ConfigurationClass(reader, beanName)); } protected final void parse(Class<?> clazz, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(clazz, beanName)); } protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName)); }
再來看看DeferredImportSelector實例是如何被處理的。
(Spring) context.annotation.ConfigurationClassParser.DefferedImportSelectorHandler私有類
和普通ConfigurationClass一樣,DefferedImportSelector最後也是先註冊到列表中,再依次嵌套處理,只不過在import前多了一個根據order排序。
// 對@Order(...)進行排序 private static final Comparator<DeferredImportSelectorHolder> DEFERRED_IMPORT_COMPARATOR = (o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getImportSelector(), o2.getImportSelector()); private class DeferredImportSelectorHandler { // ... public void process() { List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; try { if (deferredImports != null) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); // 添加到handler的configurationClasses列表中 deferredImports.forEach(handler::register); // 對handler中每個grouping的每個configClass,調用processImports() handler.processGroupImports(); } } finally { this.deferredImportSelectors = new ArrayList<>(); } } }
DONE.