Spring Boot經過包管理工具引入starter包就能夠輕鬆使用,省去了配置的繁瑣工做,這裏簡要的經過我的的理解說下Spring Boot啓動過程當中如何去自動加載配置。html
本文中使用的Spring Boot版本爲2.0.0.RELEASE
這裏主要是說自動配置大體調用流程,其餘暫不作分析java
首先,須要瞭解一件事,首先得知道有這麼一件事,而自動配置這一件事得從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,裏面就有各類自動配置的EnableAutoConfiguration
spring
此函數是一個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
//.... @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類圖瞭解它們關係
可能這裏說的不清楚,建議使用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
閱讀了別人寫的代碼,看別人爲什麼這麼寫,這裏看到的就是對於接口的活用,對於封裝以及工廠模式的應用,對於擴展,文件配置等等,本身能學到的還有不少,繼續敲代碼,看代碼,向人家學習
參考連接: https://docs.spring.io/spring-boot/docs/2.1.4.RELEASE/reference/htmlsingle/ https://www.cnblogs.com/saaav/tag/spring%20boot/