說明:
本系列基於SpringBoot 2.2.9.RELEASE 版本,對SpringBoot的原理進行分析,一共分為四節:
SpringBoot原理發現(一):建立Hello World,對pom依賴以及@SpringBootApplication註解進行分析
SpringBoot原理發現(二):分析SpringBoot自動配置原理
SpringBoot原理發現(三):通過主配置類main方法分析SpringBoot啟動配置原理
SpringBoot原理發現(四):瞭解SpringBoot啟動中的幾個重要回撥機制
Hello World
建立Hello World,主啟動類如下:
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @RestController public class HelloController{ @GetMapping("/") public String helloWorld(){ return "hello world"; } } }
1. POM檔案
1.1 父POM
<!-- 當前專案pom --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.9.RELEASE</version> <relativePath/> </parent> <!-- spring-boot-starter-parent 的父pom --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.2.9.RELEASE</version> <relativePath>../../spring-boot-dependencies</relativePath> </parent>
可以看出當前專案pom中的parent 依賴了 spring-boot-dependencies,而spring-boot-dependencies中管理的就是SpringBoot應用裡面的所有依賴版本。(如果其中沒包含,需要自定義在父pom中進行依賴)
<!-- spring-boot-dependencies 部分程式碼 --> <properties> <activemq.version>5.15.13</activemq.version> <antlr2.version>2.7.7</antlr2.version> <appengine-sdk.version>1.9.81</appengine-sdk.version> <artemis.version>2.10.1</artemis.version> <aspectj.version>1.9.6</aspectj.version> <assertj.version>3.13.2</assertj.version> <atomikos.version>4.0.6</atomikos.version> <awaitility.version>4.0.3</awaitility.version> <bitronix.version>2.1.4</bitronix.version>
.......
1.2 啟動器
SpringBoot將所有的功能場景都抽取成一個個的starter,只需要依賴某個starter,便有了相關依賴資訊及功能,這樣的starter稱之為場景啟動器。
可以通過點選官網進行檢視包含哪些starter,如下圖:
如開發web應用,只需要引入spring-boot-starter-web便有了web模組的相關功能
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ......
spring-boot-starter-web:管理了web模組需要引用的依賴檔案,如下圖:
<!-- spring-boot-starter-web.pom部分依賴-->
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.2.9.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> <version>2.2.9.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.2.9.RELEASE</version> <scope>compile</scope> </dependency>
2.主程式分析
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@RestController
public class HelloController{
@GetMapping("/")
public String helloWorld(){
return "hello world";
}
}
}
2.1 @SpringBootApplication
該註解標註在某個類上,說明這個類是SpringBoot的主配置類,SpringBoot應用應該使用這個類的main方法進行啟動。
進入該註解,可以看到@SpringBootApplication是一個組合註解,程式碼如下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {
2. 1.1 @SpringBootConfiguration
SpringBoot的配置類。進去會發現其實就是一個Spring的配置類@Configuration,而@Configuration其實就是一個元件@Component。
@SpringBootConfiguration是SpringBoot提供的,而@Configuration是Spring提供的
程式碼如下:
/** * SpringBootConfiguration */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { ..... /** * Configuration */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration {
2 .2 @EnableAutoConfiguration
作用:開啟自動配置功能
所謂開啟自動配置:以前需要提供各種配置檔案,如包掃描等,現在使用@EnableAutoConfiguration標註,SpringBoot就會開啟自動配置功能。
點開這個註解,會發現它同樣是組合註解,如下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {
2.2.1 @AutoConfigurationPackage
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
@Import(AutoConfigurationPackages.Registrar.class):向容器中匯入一個元件,元件由@Import後面傳入的類指定
AutoConfigurationPackages.Registrar.class 關鍵程式碼如下:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImport(metadata).getPackageName()); }
通過debug可以檢視該註解是標註是在主啟動類上,獲取主啟動類所在的包位置路徑,將該路徑下面的所有包掃描注入到容器中。類似spring配置檔案xml中 <context:component-scan base-package="xx.yyy" />功能。
@AutoConfigurationPackage 其實就是自動獲取主配置類下面的所有包並掃描注入進IOC容器中。
2.2.2 @Import(AutoConfigurationImportSelector.class)
同樣是往IOC容器中匯入AutoConfigurationImportSelector.class中指定的元件。
AutoConfigurationImportSelector主要程式碼:
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); }
通過debug可以看出,它會返回各種XXXAutoConfiguration。而有了這些XXXAutoConfiguration,就不需要我們在手動去編寫相關場景的注入功能元件等工作,如下圖:
這些XXXAutoConfiguration又是從何而來呢?
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; }
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader())
第一個引數:EnableAutoConfiguration.class
第二個引數:ClassLoader
進入loadFactoryNames方法,SpringBoot會掃描類路徑下的META-INF/spring.factories 檔案中定義的資料,並返回第一個引數EnableAutoConfiguration指定的值,並將這些值作為自動配置類匯入到容器中,
在後續該方法會經常出現,用於掃描spring.factories中定義的值,如下圖:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ ......
至此基本上可以看出SpringBoot是如何進行載入且工作的。說到底spring的配置檔案一個也沒少,只是SpringBoot底層將我們的配置檔案進行自動配置到容器中了,少了手動編寫大量的元件配置。