springboot啓動流程(十)springboot自動配置機制

全部文章

http://www.javashuo.com/article/p-uvudtich-bm.htmlhtml

 

正文

第七篇文章中咱們瞭解到,refresh過程將會調用ConfigurationClassPostProcessor這個後置處理器,而這個後置處理器將會去調用ConfigurationClassParser這個配置類的解析器,而第一個被解析的配置類就是咱們main方法所在的主類(主類是在refresh以前的,prepareRefresh方法加載成爲BeanDefinition到BeanFactory中的)。spring

然後,在第八篇文章中咱們主要看了看ConfigurationClassParser是怎麼解析配置類的@ComponentScan這個註解的。springboot

那麼本文將繼續從ConfigurationClassParser這個過程開始,看看parse的處理過程關於自動配置的內容。ui

 

自動配置入口

首先,咱們回到ConfigurationClassParser的parse方法。this

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方法lua

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

再跟進processConfigurationClass方法spa

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);

    //
}

這裏先作了一個判斷,是否跳過當前配置類的解析(後面會說起)。然後就是對配置類的遞歸解析,若是有父類將會遞歸解析。code

 

跟進doProcessConfigurationClass方法,咱們省略其它內容htm

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
        throws IOException {

    // 

    // 處理@Import註解
    processImports(configClass, sourceClass, getImports(sourceClass), true);

    //
    return null;
}

咱們看到,解析邏輯中包含着一個processImports方法,用於處理@Import註解。咱們知道,每個springboot程序將會註解一個@SpringBootApplication註解,這個註解是一個組合註解,咱們看看該註解。blog

@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來決定是否要進行自動配置。

相關文章
相關標籤/搜索