SpringBoot 運行原理


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 時的配置(我也暈了可能是這樣吧)。

小結

  1. SpringBoot 在啟動的時候從類路徑下的 META-INF/spring.factories 文件中獲取 @EnableAutoConfiguration 要自動裝配的配置類,將這些配置類作為 bean 導入 IoC 容器中,自動配置就生效了。
  2. 不是所有的 AutoConfiguration 自動配置類都會被裝配,還要判斷是否符合裝配的條件 @ConditionalOn...(自動配置類的注解),只有符合條件這個類才會被裝配!
  3. 在容器中導入的 AutoConfiguration 自動配置類就是當前運行場景需要的所有組件,且已經配置完成,省去了我們手動進行配置的工作!

4. 主啟動類方法

主啟動類中只有一個 main 方法和一句話

@SpringBootApplication
public class SpringBoot01HelloApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBoot01HelloApplication.class, args);
    }

}

運行主啟動類的 main 方法,開啟的是整個 SpringBoot 的服務!核心就是 SpringApplication.run 方法,它包含兩部分

  1. SpringApplication 對象的實例化
  2. run 方法的執行

查看 SpringApplication 類的構造器,可以發現它主要做了四件事

  1. 判斷應用類型是普通項目還是 Web 項目

    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
  2. 加載所有可用的初始化器,設置到 initializers 屬性中( List<ApplicationContextInitializer<?>> initializers

    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    
  3. 加載所有可用的程序監聽器,設置到 listeners 屬性中( List<ApplicationListener<?>> listeners

    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    
  4. 推斷並設置 main 方法的定義類,找到運行的主類

    this.mainApplicationClass = this.deduceMainApplicationClass();
    

至於 run 方法的執行流程以后再說吧,我已經要吐了。

5. 總結

簡單了解一下 SpringBoot 的運行原理······只能說是硬着頭皮看,看不明白也沒辦法。

一點點理解:SpringBoot 通過啟動器的依賴,判斷要自動裝配哪些配置類,這些 Spring 配置類采用的是 JavaConfig 的方式,即使用 @Configuration 注解進行配置,進行了許多默認的配置,即所謂的約定;如不手動進行配置的更改,則會按照默認的配置運行,避免了重復配置的過程,即約定大於配置

希望看出來的一點點理解是正確的吧,這種東西就應該放到最后😡!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM