Spring Boot源碼(六) - 自動裝配(下)- AutoConfigurationImportSelector

目錄

一、process

1、getAutoConfigurationMetadata

2、getAutoConfigurationEntry

1)、獲取EnableAutoConfiguration註解的exclude和excludeName屬性值

2)、獲取spring.factories中EnableAutoConfiguration的配置值

3)、removeDuplicates(去重)

4)、獲取需要排除(不自動裝配)的類

5)、checkExcludedClasses(檢查需要排除的項目)

6)、剔除排除項

6)、發送AutoConfigurationImportEvent事件

7)、創建AutoConfigurationEntry對象返回

二、selectImports


    繼續上一篇,知道了執行時機和調用順序,繼續分析兩個方法的邏輯,是怎樣實現自動裝配的。開始之前先看看其類結構(屬性)AutoConfigurationImportSelector.AutoConfigurationGroup

private static class AutoConfigurationGroup
			implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
    // 存儲註解對象關係
    private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
    // 存儲自動裝配的數據
    private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();

    private ClassLoader beanClassLoader;

    private BeanFactory beanFactory;

    private ResourceLoader resourceLoader;
    // 存儲自動裝配的,後置處理器,默認order值等信息
    private AutoConfigurationMetadata autoConfigurationMetadata;
}

一、process

@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    // 斷言判斷,肯定爲true
    Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, // 省略部分代碼);
    // 向上轉型,調用父類的方法
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
            .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
    // 添加到autoConfigurationEntries屬性中
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    // 將bean信息添加到entries(Map<String, AnnotationMetadata>)中
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

    獲取AutoConfigurationEntry類型的返回結果,組裝數據到AutoConfigurationGroup的autoConfigurationEntries和entries中。主要的方法在getAutoConfigurationMetadata中,但是在調用前會加載autoConfigurationMetadata數據。

1、getAutoConfigurationMetadata

private AutoConfigurationMetadata getAutoConfigurationMetadata() {
    if (this.autoConfigurationMetadata == null) {
        this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
    }
    return this.autoConfigurationMetadata;
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
    // String PATH = "META-INF/spring-autoconfigure-metadata.properties";
    return loadMetadata(classLoader, PATH);
}

static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
    try {
        Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
                : ClassLoader.getSystemResources(path);
        Properties properties = new Properties();
        while (urls.hasMoreElements()) {
            properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
        }
        return loadMetadata(properties);
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
    }
}

    查詢的文件路徑爲 "META-INF/spring-autoconfigure-metadata.properties",總共有488項key、value對應值。並將其值加載成爲Properties類型數據。先看看配置文件大概的內容:

org...RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration

org...CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate

org...JerseyAutoConfiguration.AutoConfigureOrder=-2147483648

    大致有三種類型(爲後續spring.factories中的自動裝配類,進行條件判斷,過濾使用):

1)、...XXAutoConfiguration.AutoConfigureAfter    誰的自動裝配的後置處理器類型

2)、...XXAutoConfiguration.ConditionalOnClass   誰的自動裝配的裝配條件

3)、...XXAutoConfiguration.AutoConfigureorder    誰的自動裝配的排序字段值

 

    再繼續看看最後加裝的類型(最好加裝爲PropertiesAutoConfigurationMetadata類型,存放到Spring的Properties類型中,就是一個Hashtable):

static AutoConfigurationMetadata loadMetadata(Properties properties) {
    return new PropertiesAutoConfigurationMetadata(properties);
}

 

2、getAutoConfigurationEntry

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
                                                           AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

1)、獲取EnableAutoConfiguration註解的excludeexcludeName屬性值

    只是默認情況下使用@EnableAutoConfiguration直接都是直接使用的@SpringBootApplication,所以獲取到的屬性值都爲默認值(爲空)。

protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
    // org.springframework.boot.autoconfigure.EnableAutoConfiguration
    String name = getAnnotationClass().getName();
    AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
    Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
            + " annotated with " + ClassUtils.getShortName(name) + "?");
    return attributes;
}

2)、獲取spring.factories中EnableAutoConfiguration的配置值

    在調用前需要查看加載的類型:

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}
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;
}

    在啓動Spring Boot項目時,分析過會加載META-INF/spring.factories中的所有配置信息。正確流程情況下會在SpringApplic

ation.run方法中,加載SpringApplicationRunListener類型時就會去加載該properties中的信息,其中有一項大致爲:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
。。。。。。

 

3)、removeDuplicates(去重)

protected final <T> List<T> removeDuplicates(List<T> list) {
    return new ArrayList<>(new LinkedHashSet<>(list));
}

    雖然比較簡單,但是是我們在寫項目的時候可以進行借鑑的方式。

4)、獲取需要排除(不自動裝配)的類

protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    Set<String> excluded = new LinkedHashSet<>();
    excluded.addAll(asList(attributes, "exclude"));
    excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
    excluded.addAll(getExcludeAutoConfigurationsProperty());
    return excluded;
}

    不僅處理了之前getAttributes加載的@EnableConfiguration上的配置值,還添加了getExcludeAutoConfigurationsProperty中,在Environment中加載的值。當然,默認情況下所有值都爲空。

5)、checkExcludedClasses(檢查需要排除的項目)

    循環遍歷排除項,如果在自動裝配項中不存在需要排除的項,則組裝檢查異常IllegalStateException並拋出。所以不能隨便寫排除項。

6)、剔除排除項

// 根據上面的排除項集合,剔除排除項
configurations.removeAll(exclusions);
// 根據加載的spring-autoconfigure-metadata.properties條件,進行排除
configurations = filter(configurations, autoConfigurationMetadata);
private List<String> filter(List<String> configurations, AutoConfigurationMetadata 
    autoConfigurationMetadata) {

    long startTime = System.nanoTime();
    String[] candidates = StringUtils.toStringArray(configurations);
    boolean[] skip = new boolean[candidates.length];
    boolean skipped = false;
    for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
        invokeAwareMethods(filter);
        boolean[] match = filter.match(candidates, autoConfigurationMetadata);
        for (int i = 0; i < match.length; i++) {
            if (!match[i]) {
                skip[i] = true;
                candidates[i] = null;
                skipped = true;
            }
        }
    }
    if (!skipped) {
        return configurations;
    }
    List<String> result = new ArrayList<>(candidates.length);
    for (int i = 0; i < candidates.length; i++) {
        if (!skip[i]) {
            result.add(candidates[i]);
        }
    }
    if (logger.isTraceEnabled()) {
        int numberFiltered = configurations.size() - result.size();
        logger.trace("Filtered " + numberFiltered + " auto configuration class in "
                + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
    }
    return new ArrayList<>(result);
}

1)、先獲取spring.factories中的AutoConfigurationImportFilter類型。

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

2)、遍歷上面的三種類型,並且回調三種類型的BeanClassLoaderAware、BeanFactoryAware、EnvironmentAware、ResourceLoaderAware接口進行賦值。

3)、再結合spring-autoconfigure-metadata.properties中加載的具體類型調用match方法進行判定。

4)、組裝過濾後的最終需要自動裝配的類型,返回。

 

6)、發送AutoConfigurationImportEvent事件

private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
    List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
    if (!listeners.isEmpty()) {
        AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
        for (AutoConfigurationImportListener listener : listeners) {
            invokeAwareMethods(listener);
            listener.onAutoConfigurationImportEvent(event);
        }
    }
}

    獲取事件監聽列表,遍歷回調Aware接口,發送AutoConfigurationImportEvent 事件。

    監聽列表也是自動裝配的spring.factories中的AutoConfigurationImportListener類型。

7)、創建AutoConfigurationEntry對象返回

    根據需要自動裝配的類型列表和需要排除的對象列表,創建AutoConfigurationEntry類型對象返回,爲後續的selectImports回調做準備。

 

二、selectImports

@Override
public Iterable<Entry> selectImports() {
    if (this.autoConfigurationEntries.isEmpty()) {
        return Collections.emptyList();
    }
    Set<String> allExclusions = this.autoConfigurationEntries.stream()
            .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream)
            .collect(Collectors.toSet());
    Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
            .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
            .collect(Collectors.toCollection(LinkedHashSet::new));
    processedConfigurations.removeAll(allExclusions);

    return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
            .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
            .collect(Collectors.toList());
}

1)、從List<AutoConfigurationEntry> autoConfigurationEntries中,獲取需要排除的HashSet

2)、從List<AutoConfigurationEntry> autoConfigurationEntries中,獲取自動裝配的List並轉化爲LinkedHashSet

3)、從自動裝配列表中剔除需要排除的項

4)、將自動裝配項進行排序,並且組裝成DeferredImportSelector.Group.Entry類型,返回。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章