所有文章
https://www.cnblogs.com/lay2017/p/11478237.html
正文
在第七篇文章中我們了解到,refresh過程將會調用ConfigurationClassPostProcessor這個后置處理器,而這個后置處理器將會去調用ConfigurationClassParser這個配置類的解析器,而第一個被解析的配置類就是我們main方法所在的主類(主類是在refresh之前的,prepareRefresh方法加載成為BeanDefinition到BeanFactory中的)。
而后,在第八篇文章中我們主要看了看ConfigurationClassParser是怎么解析配置類的@ComponentScan這個注解的。
那么本文將繼續從ConfigurationClassParser這個過程開始,看看parse的處理過程關於自動配置的內容。
自動配置入口
首先,我們回到ConfigurationClassParser的parse方法。
public void parse(Set<BeanDefinitionHolder> configCandidates) { 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); } } // 處理自動配置的入口 this.deferredImportSelectorHandler.process(); }
springboot解析過程將從解析main方法所在的主類開始,所以我們先跟進parse方法
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName)); }
再跟進processConfigurationClass方法
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { // 判斷當前配置類是否應該跳過解析 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } // 省略 // 向父類遞歸解析 SourceClass sourceClass = asSourceClass(configClass); do { // 解析當前配置類的核心邏輯 sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); // }
這里先做了一個判斷,是否跳過當前配置類的解析(后面會提及)。而后就是對配置類的遞歸解析,如果有父類將會遞歸解析。
跟進doProcessConfigurationClass方法,我們省略其它內容
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // // 處理@Import注解 processImports(configClass, sourceClass, getImports(sourceClass), true); // return null; }
我們看到,解析邏輯中包含着一個processImports方法,用於處理@Import注解。我們知道,每一個springboot程序將會注解一個@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 { // 省略 }
@SpringBootApplication注解組合了@EnableAutoConfiguration注解,我們再看看@EnableAutoConfiguration注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { // 省略 }
可以看到,@Import注解導入了一個AutoConfigurationImportSelector類。
我們再回到doProcessConfigurationClass方法
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // // 處理@Import注解 processImports(configClass, sourceClass, getImports(sourceClass), true); // return null; }
processImports之前,會調用getImports方法獲取當前配置類的@Import,也包含組合的注解。所以@SpringBootApplication組合的@Import注解導入的配置類AutoConfigurationImportSelector將在這里被獲取。
我們跟進getImport方法看看
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException { Set<SourceClass> imports = new LinkedHashSet<>(); Set<SourceClass> visited = new LinkedHashSet<>(); collectImports(sourceClass, imports, visited); return imports; }
再跟進collectImports看看是怎么搜集類的
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注解的,遞歸看看有沒有組合@Import注解 if (!annName.equals(Import.class.getName())) { collectImports(annotation, imports, visited); } } imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); } }
很顯然,搜集過程將對所有注解遞歸處理,這樣我們就獲得了main方法所在主類的所有@Import導入的類。
再回到doProcessConfigurationClass方法
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // // 處理@Import注解 processImports(configClass, sourceClass, getImports(sourceClass), true); // return null; }
跟進processImports方法
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) { // if (checkForCircularImports && isChainedImportOnStack(configClass)) { // } else { // try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); // if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { // } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // } else { // processConfigurationClass(candidate.asConfigClass(configClass)); } } } // } }
這里遍歷了我們getImports方法獲取到的類,但是目前我們還只有AutoConfiguratonImportSelector這個類。這個類實現了DeferredImportSelector接口,所以我們繼續跟進handle方法
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) { DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector); if (this.deferredImportSelectors == null) { // } else { // 添加到集合 this.deferredImportSelectors.add(holder); } }
AutoConfigurationImportSelector被包裝已經添加到了集合中。那么這個被添加到集合中的類是什么時候被處理的呢?
我們回到本文最開始的代碼片段,parse方法
public void parse(Set<BeanDefinitionHolder> configCandidates) { 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); } } // 處理自動配置的入口 this.deferredImportSelectorHandler.process(); }
可以看到,parse方法的最后一行,調用了process方法,將會對這些類進行處理,這也就是自動配置的入口方法了。
處理AutoConfigurationImportSelector
我們跟進process方法,看看處理過程
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中 deferredImports.forEach(handler::register); // 處理handler中的導入類 handler.processGroupImports(); } } finally { // } }
這里先將導入類調用handler的register方法進行注冊,然后集中處理。我們稍微瞄一眼register方法
public void register(DeferredImportSelectorHolder deferredImport) { Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup(); DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent((group != null ? group : deferredImport), key -> new DeferredImportSelectorGrouping(createGroup(group))); // 添加到group中 grouping.add(deferredImport); this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); }
跟進add方法
private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>(); public void add(DeferredImportSelectorHolder deferredImport) { this.deferredImports.add(deferredImport); }
添加到一個集合中存放起來
我們回到process方法
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中 deferredImports.forEach(handler::register); // 處理handler中的導入類 handler.processGroupImports(); } } finally { // } }
register完畢以后,將會processGroupImports,我們跟進processGroupImports方法
public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { // 獲取所有AutoConfigurationImportSelector返回的待處理的配置類,並遍歷 grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get( entry.getMetadata()); try { // 處理所有配置類 processImports(configurationClass, asSourceClass(configurationClass), asSourceClasses(entry.getImportClassName()), false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); } }); } }
AutoConfigurationImportSelector需要進行自動配置的類將會在這里的getImports方法中返回,而后processImports方法將會處理所有這些需要自動配置的類
我們先跟進getImports方法,看看是怎么獲取所有待處理的配置類的
public Iterable<Group.Entry> getImports() { for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { // 處理生成結果 this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } // 返回結果 return this.group.selectImports(); }
我們主要看process方法,進入到AutoConfigurationGroup的process方法
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } }
再跟進getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { // AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // // 過濾 configurations = filter(configurations, autoConfigurationMetadata); // return new AutoConfigurationEntry(configurations, exclusions); }
繼續跟進getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); // return configurations; }
我們看到一個熟悉的方法loadFactoryNames(不熟悉的話,請閱讀輔助內容),看看getSpringFactoriesLoaderFactoryClass返回什么
protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
我們可以隨機打開一個spring.factories看看EnableAutoConfiguration作為key的配置
可以看到spring.factories中將需要進行自動配置的類作為value配置在這里,所以getCandidateConfigurations方法將會把這些配置類返回。我們再回到getAutoConfigurationEntry方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { // AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // // 過濾 configurations = filter(configurations, autoConfigurationMetadata); // return new AutoConfigurationEntry(configurations, exclusions); }
獲取完configurations后,將會進行一次過濾操作,這樣可以避免大量的不需要配置的類被加載。
再回到AutoConfiguration的process方法
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>(); public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } }
我們調用getAutoConfigurationEntry獲得需要自動配置的類,然后再這里會被添加到一個Map集合中存放起來。
到這里,AutoConfigurationImportSelector的getImports方法的process過程就結束了。我們回到getImports方法
public Iterable<Group.Entry> getImports() { for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { // 處理生成結果 this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } // 返回結果 return this.group.selectImports(); }
process方法獲取了Imports,而selectImports將返回結果。
到這里,我們的getImports方法就獲取了可能需要進行自動配置的類,回到DeferredImportSelectorGroupingHandler類的processGroupImports方法
public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { // 獲取所有AutoConfigurationImportSelector返回的待處理的配置類,並遍歷 grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get( entry.getMetadata()); try { // 處理所有配置類 processImports(configurationClass, asSourceClass(configurationClass), asSourceClasses(entry.getImportClassName()), false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); } }); } }
跟進processImports
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) { // if (checkForCircularImports && isChainedImportOnStack(configClass)) { // } else { // try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { // } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // } else { // 處理配置類 processConfigurationClass(candidate.asConfigClass(configClass)); } } } // } }
導入的類作為配置類來處理,跟進processConfigurationClass方法
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { // 是否要進行配置解析 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } // SourceClass sourceClass = asSourceClass(configClass); do { // 解析的邏輯 sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); // }
我們久違的processConfigurationClass方法,一開始我們關注的是doProcessConfigurationClass看它解析過程的。現在我們來看看shouldSkip方法,看看是怎么判斷當前配置類是否要進行解析的。
跟進shouldSkip方法
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) { // List<Condition> conditions = new ArrayList<>(); // 獲取配置類中所有@Conditional以及組合@Conditional的條件 for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this.context.getClassLoader()); conditions.add(condition); } } // // 遍歷這些條件 for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } // 判斷條件是否不匹配 if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) { return true; } } return false; }
shouldSkip方法,將會拿到當前配置類的所有@Conditional或者組合了@Conditional的注解,並將注解生成Condition條件,再遍歷這些條件看是否有不滿足條件的將返回true。
總結
springboot的自動配置將從解析main方法所在的主類開始,ConfigurationClassParser在解析@Import的時候會獲取到AutoConfigurationImportSelector類。AutoConfigurationImportSelector將會獲取到所有可能需要進行自動配置的類,而后每個配置類將被和main方法所在的主類一樣准備解析,在解析之前會根據像@Conditional或者組合@Conditional的注解來生成判斷條件Condition,根據是否滿足Condition來決定是否要進行自動配置。