個人Spring Boot學習記錄(一):自動配置的大體調用過程

1. 背景

Spring Boot經過包管理工具引入starter包就能夠輕鬆使用,省去了配置的繁瑣工做,這裏簡要的經過我的的理解說下Spring Boot啓動過程當中如何去自動加載配置。html

本文中使用的Spring Boot版本爲2.0.0.RELEASE
這裏主要是說自動配置大體調用流程,其餘暫不作分析java

2. 主要內容

2.1. spring.factories

首先,須要瞭解一件事,首先得知道有這麼一件事,而自動配置這一件事得從META-INF/spring.factories提及。其本質相似properties文件,一種key-value型的文件。
在Spring Boot的官方文檔中,Creating Your Own Auto-configuration裏面,它能夠掃描加載META-INF/spring.factories中的EnableAutoConfiguration爲key的配置類。引入一個依賴,例如:web

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

這個starter只是引入了其必須的依賴,沒有作任何工做,最主要的是有個依賴爲spring-boot-starter,這裏包含了一個spring.factories,裏面就有各類自動配置的EnableAutoConfigurationspring

2.2. 怎麼加載EnableAutoConfiguration

2.2.1 SpringApplication.run

此函數是一個Spring Boot項目的入口,這裏與@SpringBootApplication有很大的關聯。數組

Spring Boot項目通常的啓動代碼以下:springboot

@SpringBootApplication
public class SpringBootDemoApplication {
    public static void main(String[] args) {
        //主要提供了一個靜態函數run來調用
        SpringApplication.run(SpringBootDemoApplication.class, args);
    }
}

再看run函數app

public static ConfigurableApplicationContext run(Class<?> primarySource,
            String... args) {
        return run(new Class<?>[] { primarySource }, args); // 
    }

    public static ConfigurableApplicationContext run(Class<?>[] primarySources,
            String[] args) {
        return new SpringApplication(primarySources).run(args);
    }
    
    // 最終被調用的run函數
    public ConfigurableApplicationContext run(String... args) { 
        //......        
        context = createApplicationContext();           
        prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
        refreshContext(context);
        //......    
        return context;
    }

以上中,最終是調用了org.springframework.boot.SpringApplication#run(java.lang.String...)方法,此方法主要是準備了Environment和ApplicationContext。而ApplicationContext就是Spring項目核心的東西,那與自動配置又有什麼關係,這裏就須要回去看下@SpringBootApplication註解ide

2.2.2 @SpringBootApplication

//....
@EnableAutoConfiguration
public @interface SpringBootApplication {
    //.....
}
//在上面@SpringBootApplication的註解代碼中,有個@EnableAutoConfiguration
//.....
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    //...
}

上面爲SpringBootApplication和EnableAutoConfiguration註解的部分代碼,其中,在EnableAutoConfiguration註解中又有@Import(AutoConfigurationImportSelector.class),這裏的@Import是Spring context的內容,與後面的內容中org.springframework.context.annotation.ConfigurationClassParser類有關聯,目前要知道其主要功能就是將AutoConfigurationImportSelector加載至上下文中。在瞭解AutoConfigurationImportSelector源碼以前,咱們須要先知道SpringFactoriesLoader,這就是一個META-INF/spring.factories文件加載器。 其源碼可看org.springframework.core.io.support.SpringFactoriesLoader函數

如今,咱們看至AutoConfigurationImportSelector的源碼:spring-boot

/**
 * {@link DeferredImportSelector} to handle {@link EnableAutoConfiguration
 * auto-configuration}. This class can also be subclassed if a custom variant of
 * {@link EnableAutoConfiguration @EnableAutoConfiguration}. is needed.
 * 
 * 主要的意思爲:DeferredImportSelector可以去處理 EnableAutoConfiguration自動配置類Import工做
 */
public class AutoConfigurationImportSelector
        implements DeferredImportSelector,... {
    
    /**
     * 返回一個須要被加載至Spring上下文的的類名數組
     */
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //....
        List<String> configurations = getCandidateConfigurations(annotationMetadata,
                    attributes);
        //....
        return StringUtils.toStringArray(configurations);
    }

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
            AnnotationAttributes attributes) {
            //經過SpringFactoriesLoader加載出全部的EnableAutoConfiguration類
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
                getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
        return configurations;
    }
    
    /**
     * 返回了一個讓SpringFactoriesLoader加載的Class,就是EnableAutoConfiguration
     */
    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }
}

那麼到底是誰去調用了org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports並加載了那些EnableAutoConfiguration。這裏的步驟比較多,咱們就從selectImports倒序提及,這裏分爲幾點來講:

  • 首先,AutoConfigurationImportSelector 繼承了接口ImportSelector
  • org.springframework.context.annotation.ConfigurationClassParser類經過接口org.springframework.context.annotation.ImportSelector調用了selectImports,這裏調用的方法分別爲#processDeferredImportSelectors#processImports,最終的指向都是#parse(Set<BeanDefinitionHolder>)方法。這裏須要說明的是#processImports方法就是對於處理@Import註解的相關方法,該類的源碼中註釋有說明。
  • org.springframework.context.annotation.ConfigurationClassParser#parse(Set<BeanDefinitionHolder>)倒是org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions調用,而#processConfigBeanDefinitions爲自身方法所調用。
  • ConfigurationClassPostProcessor調用的源頭是類org.springframework.context.support.PostProcessorRegistrationDelegate,這個類中有兩個公共的調用方法。
  • 最後由org.springframework.context.support.AbstractApplicationContext#refresh調用
  • org.springframework.context.support.AbstractApplicationContext#refresh方法在org.springframework.boot.SpringApplication#run被調用了

所以,這裏就與咱們上面介紹的SpringApplication.run產生了聯繫,就是經過其調用了抽象上下文AbstractApplicationContext的refresh方法,從而產生了上面的一系列步驟。

能夠看下UML類圖瞭解它們關係
UML
可能這裏說的不清楚,建議使用IDE進行debug看源碼。並且這裏對於Spring Context的內容沒有展開,本人也只知其一;不知其二(或者說不解,不瞭解),望見諒,有須要能夠參考如下文章

https://docs.spring.io/spring/docs/5.2.0.BUILD-SNAPSHOT/spring-framework-reference/core.html#spring-core
https://www.cnblogs.com/davidwang456/p/5717972.html
https://blog.csdn.net/yangyangiud/article/details/79835594

3. 總結

閱讀了別人寫的代碼,看別人爲什麼這麼寫,這裏看到的就是對於接口的活用,對於封裝以及工廠模式的應用,對於擴展,文件配置等等,本身能學到的還有不少,繼續敲代碼,看代碼,向人家學習

參考連接: https://docs.spring.io/spring-boot/docs/2.1.4.RELEASE/reference/htmlsingle/ https://www.cnblogs.com/saaav/tag/spring%20boot/

相關文章
相關標籤/搜索