SpringBoot 源碼解析 (五)----- Spring Boot的核心能力 - 自動配置源碼解析


在上一篇博客中分析了springBoot啟動流程,大體的輪廓只是冰山一角。今天就來看一下springBoot的亮點功能:自動化裝配功能。

先從@SpringBootApplication開始。在啟動流程章節中,我們講述了SpringBoot2大致的啟動步驟,並進行了源碼詳解。但是在刷新容器這塊並未展開,refreshContext(context);簡單的一行代碼,背后卻做了太多事情。所以為了不喧賓奪主,本篇也盡量選取和注解@SpringBootApplication有關的方法講解。

springBoot啟動類加載

首先加載springBoot啟動類注入到spring容器中beanDefinitionMap中,看下prepareContext方法中的load方法:load(context, sources.toArray(new Object[0]));
跟進該方法最終會執行BeanDefinitionLoader的load方法:

private int load(Object source) {
    Assert.notNull(source, "Source must not be null");
    //如果是class類型,啟用注解類型
    if (source instanceof Class<?>) { return load((Class<?>) source); } //如果是resource類型,啟用xml解析
    if (source instanceof Resource) {
        return load((Resource) source);
    }
    //如果是package類型,啟用掃描包,例如:@ComponentScan
    if (source instanceof Package) {
        return load((Package) source);
    }
    //如果是字符串類型,直接加載
    if (source instanceof CharSequence) {
        return load((CharSequence) source);
    }
    throw new IllegalArgumentException("Invalid source type " + source.getClass());
}

繼續跟進load(Class<?> source)方法:

上述方法判斷啟動類中是否包含@component注解,可我們的啟動類並沒有該注解。繼續跟進會發現,AnnotationUtils判斷是否包含該注解是通過遞歸實現,注解上的注解若包含指定類型也是可以的。

啟動類中包含@SpringBootApplication注解,進一步查找到@SpringBootConfiguration注解,然后查找到@Component注解,最后會查找到@Component注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component public @interface Configuration {
}

在查找到@Component注解后,表面該對象為spring bean,然后會將其信息包裝成 beanDefinitaion ,添加到容器的 beanDefinitionMap中。如下:

如此一來,我們的啟動類就被包裝成AnnotatedGenericBeanDefinition了,后續啟動類的處理都基於該對象了。

@EnableAutoConfiguration

@SpringBootApplication注解中包含了自動配置的入口注解:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}

我們跟進去看看@EnableAutoConfiguration

@AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {}

@AutoConfigurationPackage

  • 自動配置包注解
@Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage {}

@Import(AutoConfigurationPackages.Registrar.class):默認將主配置類(@SpringBootApplication)所在的包及其子包里面的所有組件掃描到Spring容器中。如下

@Order(Ordered.HIGHEST_PRECEDENCE)
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
         //默認將會掃描@SpringBootApplication標注的主配置類所在的包及其子包下所有組件
        register(registry, new PackageImport(metadata).getPackageName());
    }

    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.<Object>singleton(new PackageImport(metadata));
    }
}

@Import(EnableAutoConfigurationImportSelector.class)

EnableAutoConfigurationImportSelector: 導入哪些組件的選擇器,將所有需要導入的組件以全類名的方式返回,這些組件就會被添加到容器中。

 1 //EnableAutoConfigurationImportSelector的父類:AutoConfigurationImportSelector
 2 @Override
 3 public String[] selectImports(AnnotationMetadata annotationMetadata) {
 4     if (!isEnabled(annotationMetadata)) {
 5         return NO_IMPORTS;
 6     }
 7     try {
 8         AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
 9             .loadMetadata(this.beanClassLoader);
10         AnnotationAttributes attributes = getAttributes(annotationMetadata);
11         List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); 12         configurations = removeDuplicates(configurations);
13         configurations = sort(configurations, autoConfigurationMetadata);
14         Set<String> exclusions = getExclusions(annotationMetadata, attributes);
15         checkExcludedClasses(configurations, exclusions);
16         configurations.removeAll(exclusions);
17         configurations = filter(configurations, autoConfigurationMetadata);
18         fireAutoConfigurationImportEvents(configurations, exclusions);
19         return configurations.toArray(new String[configurations.size()]);
20     }
21     catch (IOException ex) {
22         throw new IllegalStateException(ex);
23     }
24 }

我們主要看第11行List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);會給容器中注入眾多的自動配置類(xxxAutoConfiguration),就是給容器中導入這個場景需要的所有組件,並配置好這些組件。獲取這些組件后,還要過濾一下這些組件,我們跟進去看看

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
            AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    //...
    return configurations;
}

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    try {
        //從類路徑的META-INF/spring.factories中加載所有默認的自動配置類
        Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        List<String> result = new ArrayList<String>();
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
            //獲取EnableAutoConfiguration指定的所有值,也就是EnableAutoConfiguration.class的值
            String factoryClassNames = properties.getProperty(factoryClassName);
            result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
        }
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

SpringBoot啟動的時候從類路徑下的 META-INF/spring.factories中獲取EnableAutoConfiguration指定的值,並將這些值作為自動配置類導入到容器中,自動配置類就會生效,最后完成自動配置工作。EnableAutoConfiguration默認在spring-boot-autoconfigure這個包中,如下圖

最終有96個自動配置類被加載並注冊進Spring容器中

我們也可以將需要自動配置的Bean寫入這個文件

自定義starter

首先定義一個配置類模塊:

@Configuration
@ConditionalOnProperty(name = "enabled.autoConfituration", matchIfMissing = true) public class MyAutoConfiguration {

    static {
        System.out.println("myAutoConfiguration init...");
    }

    @Bean
    public SimpleBean simpleBean(){
        return new SimpleBean();
    }

}

然后定義一個starter模塊,里面無需任何代碼,pom也無需任何依賴,只需在META-INF下面建一個 spring.factories文件,添加如下配置:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
spring.study.startup.bean.MyAutoConfiguration

如圖所示:

最后只需在啟動類項目的pom中引入我們的 starter 模塊即可。

原理

最終在AutoConfigurationImportSelector解析spring.factories文件:

springBoot為我們提供的配置類有180多個,但是我們不可能會全部引入。按條件注解 @Conditional或者@ConditionalOnProperty等相關注解進行判斷,決定是否需要裝配。

我們自定義的配置類也是以相同的邏輯進行裝配,我們指定了以下注解:

@ConditionalOnProperty(name = "enabled.autoConfituration", matchIfMissing = true)

默認為 true,所以自定義的starter成功執行。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM