SpringBoot 運行原理
自從寫了第一個 SpringBoot 程序后摸了幾天魚,現在回來研究一下 SpringBoot 的運行原理!
1. 依賴文件pom
之前 SpringBoot 創建的是一個 Maven 項目,所以對應的配置文件 pom.xml 中肯定包含了項目需要的所有依賴。點進 pom.xml 查看,發現只有幾個啟動器依賴,不過可以看到這個項目存在一個父項目 spring-boot-starter-parent
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
點進這個父項目查看,發現沒有 dependencies
部分,只有文件導出和插件等 build
配置;但它又依賴了一個父項目 spring-boot-dependencies
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.4</version>
</parent>
再次點進這個父項目,看到了2000多行的依賴配置,這才是項目需要的 jar 包依賴所在!其中,給許多依賴都配置了對應的版本,如
<aspectj.version>1.9.7</aspectj.version>
引用時直接引用配置的版本號
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
所以我們在引入一些依賴的時候,不需要指定版本號,就是因為在依賴配置中已經選好了!但引入依賴配置中沒有的依賴時,仍需指定其版本號。
2. 啟動器
回到最初的 pom 文件上,可以看到 SpringBoot 啟動器的依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
啟動器的作用就是加載模塊的運行環境,如上面的 spring-boot-starter
就加載了 SpringBoot 的運行環境,spring-boot-starter-web
就加載了 Web 模塊的運行環境!
SpringBoot 把模塊的運行環境都提取為啟動器,需要運行什么模塊只需引入對應的啟動器,就可以引入需要的所有依賴了!
3. 主啟動類注解
創建完 SpringBoot 項目時,可以看到項目自帶了一個帶有 main 方法的類,即主啟動類
@SpringBootApplication
public class SpringBoot01HelloApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBoot01HelloApplication.class, args);
}
}
先從注解分析一下主啟動類是個什么東西!
3.1 @SpringBootApplication
@SpringBootApplication
:標注在類上表明這個類是 SpringBoot 的主啟動類,SpringBoot 就會運行這個類的 main 方法來啟動 SpringBoot 應用。
點進這個注解,又可以看到許多其他注解,其中有三個重要的
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication
將主啟動類上的 @SpringBootApplication
注解換成這三個注解,程序依然能夠運行;不過為了方便,還是直接用 @SpringBootApplication
就行了!
其中,@ComponentScan
在 Spring 中已經見過了,它是一個非常重要的注解,作用為自動掃描並加載符合條件的組件( Bean )。
3.2 @SpringBootConfiguration
@SpringBootConfiguration
:標注在類上表明這個類是 SpringBoot 的配置類。
點進這個注解,可以看到 @Configuration
注解
@Configuration
public @interface SpringBootConfiguration
說明標注了該注解的類就是一個配置類,對應 Spring 中的 XML 配置文件!
繼續深入,又看到了 @Component
注解
@Component
public @interface Configuration
這就說明,主啟動類也是 Spring 中的組件,它負責的就是啟動應用!
3.3 @EnableAutoConfiguration
@EnableAutoConfiguration
:標注在類上表明開啟自動配置功能,將所有符合條件的 @Configuration
配置都創建為 bean,並加載到當前 SpringBoot 的 IoC 容器中。
點進這個注解,可以看到
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration
點進其中的 @AutoConfigurationPackage
注解,可以看到 @import
注解
@Import({Registrar.class})
public @interface AutoConfigurationPackage
即 @AutoConfigurationPackage
注解的功能是由 @Import
注解實現的,主要作用就是將主啟動類所在包及所有子包下的組件到掃描到 Spring 的 IoC 容器中,這也是為什么新建的包要和主啟動類同級的原因了!
另一個注解 @Import(AutoConfigurationImportSelector.class)
才是最關鍵的。通過AutoConfigurationImportSelector
類,@EnableAutoConfiguration
可以讓 SpringBoot 應用將所有符合條件的 @Configuration
配置都創建為 bean,並加載到當前 SpringBoot 的 IoC 容器中。
在 AutoConfigurationImportSelector
類中,可以看到獲取候選配置的方法 getCandidateConfigurations
// 獲得候選的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 這里返回的就是一開始看到的啟動自動導入配置文件的注解類 EnableAutoConfiguration
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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
方法以獲取配置類
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
...
String factoryTypeName = factoryType.getName();
// 又調用了 loadSpringFactories 方法
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
上面又再次調用了同一個類下的 loadSpringFactories
方法,這個方法有點長,不過主要做了兩件事
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
// 獲取名為 "META-INF/spring.factories" 的資源
// ...
// 將讀取到的資源遍歷,封裝為一個 properties
// ...
}
到現在我也看暈了,不過可以看到一個出現多次的名字 META-INF/spring.factories
,搜索這個文件,發現它在 spring-boot-autoconfigure-2.5.4.jar
里面,看來和自動裝配關系匪淺!看看它都有什么東西

到這估計就有點恍然大悟了,spring.factories
中包含了所有 SpringBoot 要自動裝配的配置類,通過自動讀取它們並裝配,才實現了 SpringBoot 不需要我們進行什么配置也能直接運行的效果!
例如,在里面找到個熟悉的關於 WebMVC 的配置
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
點進這個配置類,可以看到熟悉的前綴后綴、靜態資源過濾等 WebMVC 的配置,即完成了之前使用 SpringMVC 時的配置(我也暈了可能是這樣吧)。
小結
- SpringBoot 在啟動的時候從類路徑下的
META-INF/spring.factories
文件中獲取@EnableAutoConfiguration
要自動裝配的配置類,將這些配置類作為 bean 導入 IoC 容器中,自動配置就生效了。 - 不是所有的
AutoConfiguration
自動配置類都會被裝配,還要判斷是否符合裝配的條件@ConditionalOn...
(自動配置類的注解),只有符合條件這個類才會被裝配! - 在容器中導入的
AutoConfiguration
自動配置類就是當前運行場景需要的所有組件,且已經配置完成,省去了我們手動進行配置的工作!
4. 主啟動類方法
主啟動類中只有一個 main 方法和一句話
@SpringBootApplication
public class SpringBoot01HelloApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBoot01HelloApplication.class, args);
}
}
運行主啟動類的 main 方法,開啟的是整個 SpringBoot 的服務!核心就是 SpringApplication.run
方法,它包含兩部分
SpringApplication
對象的實例化run
方法的執行
查看 SpringApplication
類的構造器,可以發現它主要做了四件事
-
判斷應用類型是普通項目還是 Web 項目
this.webApplicationType = WebApplicationType.deduceFromClasspath();
-
加載所有可用的初始化器,設置到 initializers 屬性中(
List<ApplicationContextInitializer<?>> initializers
)this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
-
加載所有可用的程序監聽器,設置到 listeners 屬性中(
List<ApplicationListener<?>> listeners
)this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
-
推斷並設置 main 方法的定義類,找到運行的主類
this.mainApplicationClass = this.deduceMainApplicationClass();
至於 run
方法的執行流程以后再說吧,我已經要吐了。
5. 總結
簡單了解一下 SpringBoot 的運行原理······只能說是硬着頭皮看,看不明白也沒辦法。
一點點理解:SpringBoot 通過啟動器的依賴,判斷要自動裝配哪些配置類,這些 Spring 配置類采用的是 JavaConfig 的方式,即使用 @Configuration
注解進行配置,進行了許多默認的配置,即所謂的約定;如不手動進行配置的更改,則會按照默認的配置運行,避免了重復配置的過程,即約定大於配置。
希望看出來的一點點理解是正確的吧,這種東西就應該放到最后😡!