天天看點

【Spring boot】自動配置的開啟原理

本文結論

  • 源碼使用spring boot2.6.6版本
  • 開始自動配置的核心注解:@EnableAutoConfiguration
  • @EnableAutoConfiguration中使用了@Import(AutoConfigurationImportSelector.class)。
  • AutoConfigurationImportSelector實作了DeferredImportSelector這個接口,Spring容器在啟動時,會在解析完其他所有程式員定義的配置類之後,來調用AutoConfigurationImportSelector中的selectImports方法,然後把該方法傳回的類名對應的類作為配置類進行解析。
  • selectImports方法中通過spi的方式,找到所有的自動配置類

spring boot開啟方式

  • 一般使用@SpringBootApplication這個注解!
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class);
    }
}      

@SpringBootApplication注解詳細資訊

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

// 下面這三個是最核心的!可以認為@SpringBootApplication是一個三合一注解。直接寫這三個注解放在核心啟動類上也是可以使用的!
@SpringBootConfiguration // 标記目前是一個配置類
@EnableAutoConfiguration // 開啟自動配置

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

開啟自動配置的核心注解:@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

// 将主程式類所在包及所有子包下的元件到掃描到 spring 容器中。
@AutoConfigurationPackage
// 最核心的,自動配置的處理類!
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ......
}      
  • @Import(AutoConfigurationImportSelector.class),Import了自動配置的核心處理類

自動配置處理類:AutoConfigurationImportSelector

  • AutoConfigurationImportSelector實作了DeferredImportSelector這個接口,Spring容器在啟動時,會在解析完其他所有程式員定義的配置類之後,來調用AutoConfigurationImportSelector中的selectImports方法,然後把該方法傳回的類名對應的類作為配置類進行解析。
  • AutoConfigurationImportSelector中的selectImports會利用SpringFactoriesLoader找到所有的META-INF/spring.factories檔案中key為EnableAutoConfiguration.class的value值,也就是衆多自動配置類的類名。
// 實作了DeferredImportSelector這個接口!
public class AutoConfigurationImportSelector
        implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
        BeanFactoryAware, EnvironmentAware, Ordered {
    ......
    // 會在@Configuration都解析完成後執行(解析完程式員自定義的類之後執行)
    // annotationMetadata這個對象是我們main方法的資訊
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // 判斷是否開啟自動配置:spring.boot.enableautoconfiguration配置的值為true(預設值為true)。沒有開啟的時候傳回一個空數組。
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }

        // 擷取自動配置類(spring.factories中導入的)
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        // 傳回配置類的類名
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
    ......

    /**
     * 擷取自動配置類(spring.factories中導入的)
     */
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        // 判斷是否開啟自動配置:spring.boot.enableautoconfiguration配置的值為true(預設值為true)。沒有開啟的時候傳回一個空對象。
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        // 擷取@EnableAutoConfiguration的屬性值(exclude,excludeName)。作用是擷取需要排除的自動配置類。
        AnnotationAttributes attributes = getAttributes(annotationMetadata);

        // 擷取spring.factories檔案中所有的org.springframework.boot.autoconfigure.EnableAutoConfiguration的值
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

        // 按照類名去重。每個jar包都可能有spring.factories中進行了配置,内容一緻進行去重。
        // 去重代碼為:return new ArrayList<>(new LinkedHashSet<>(list));
        configurations = removeDuplicates(configurations);

        // 擷取需要排除的自動配置類。通過@EnableAutoConfiguration中的倆個屬性(exclude,excludeName)進行指定。也可以通過屬性spring.autoconfigure.exclude進行配置(逗号分隔)。
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);

        // 排除操作
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);

        // 擷取spring.factories中的AutoConfigurationImportFilter進行過濾
        // 拿到條件注解過濾:OnBeanCondition、OnClassCondition、OnWebApplicationCondition
        // 使用spring-autoconfigure-metadata.properties(編譯的時候生成的檔案!裡面存一下條件注解對應關系)中的配置過濾。核心作用是加快啟動速度!
        configurations = getConfigurationClassFilter().filter(configurations);

        // 釋出一個事件,可以進行日志的列印(日志級别需要trans),展示出那些自動配置了,那些沒有自動配置,因為什麼導緻無法進行自動配置...
        fireAutoConfigurationImportEvents(configurations, exclusions);

        // 最終的傳回,全部是合格的自動配置類!
        return new AutoConfigurationEntry(configurations, exclusions);
    }

    /**
     * 擷取spring.factories檔案中所有的org.springframework.boot.autoconfigure.EnableAutoConfiguration的值
     */
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // spi的方式找到spring.factories檔案中所有的org.springframework.boot.autoconfigure.EnableAutoConfiguration的值
        // getSpringFactoriesLoaderFactoryClass()的邏輯是return EnableAutoConfiguration.class;
        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;
    }
}      

結束語

  • 你的點贊是我提高文章品質最大的動力!!!
  • 擷取更多本文的前置知識文章,以及新的有價值的文章,讓我們一起成為架構師!
  • 目前已經完成了并發程式設計、MySQL、spring源碼、Mybatis的源碼。可以在公衆号下方菜單點選檢視之前的文章!
  • 這個公衆号的所有技術點,會分析的很深入!
  • 這個公衆号,無廣告!!!