1. 程式人生 > 實用技巧 >最通俗易懂,最詳細的springboot自動配置原理解析

最通俗易懂,最詳細的springboot自動配置原理解析

tips:

​ 從springboot的入門案例中,我們可以體會到springboot的便捷之處,使用Spring Initializer建立一個專案,然後寫一個controller層就可以執行起來,我們啥也沒配置,沒配置tomcat、沒配置mvc、沒配置spring。。。。因為springboot底層都幫我們配置好了,而springboot的精髓就在於自動配置

然後不得不提一下springboot的四大特性:

  • 自動裝配
  • Starter新增專案依賴
  • Spring Boot CLI與Groovy的高效配合
  • Spring Boot Actuator

本文主要講解前兩點(重點為自動裝配

Starter新增專案依賴:

先從入門案例的pom.xml開始

springboot的專案中都會存在一個父依賴,如下

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.2.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

該父專案為所有spring-boot-starter的父專案

父專案有什麼用?用來做依賴管理,託管子專案,按住Ctrl+滑鼠左鍵點進去一探究竟

點進去之後發現spring-boot-starter的父專案,還存在一個依賴。

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.3.2.RELEASE</version>
</parent>

再次點進去spring-boot-dependencies會發現下圖所示:

定義著所有依賴的版本和管理

所以我們能得出第一個結論:

spring-boot-dependencies存放著SpringBoot的核心依賴,管理springboot應用裡面的所有依賴版本。所以以後引入一些SpringBoot依賴的時候,不需要指定版本,但是沒有在spring-boot-dependencies當中管理的需要宣告版本號。

那麼這些依賴是如何導進來的呢?

再回到pom.xml中,pom.xml中還匯入瞭如下依賴

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

怎麼理解spring-boot-starter-web呢?

可以拆分為兩部分來看spring-boot-starterweb

spring-boot-starter :spring的場景啟動器

springboot將所有的功能場景都抽取出來,做成一個個starters(啟動器),只需要在專案中引入這些starters相關場景的所有依賴都會匯入進來,而且版本會自動控制。

Starters官方解釋為一系列依賴描述的組合,可以通過匯入這些Starters就會有相應的依賴

官網中:

有aop面向切面程式設計,amqp高階訊息佇列。。。需要啥導啥就完事了

再點進 spring-boot-starter-web 看看,聲明瞭以下依賴:

<dependencies>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
   </dependency>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
   </dependency>
   <dependency>	做資料校驗的
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
   </dependency>
   <dependency> 
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
   </dependency>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
   </dependency>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
   </dependency>
</dependencies>

原來是幫我們匯入了web模組正常執行所需要依賴的元件

這個比較簡單,只是開胃菜,我們再看下一個知識點

自動配置

每次啟動都要通過這個主程式來啟動,這個主程式上放了一個註解@SpringBootApplication,此註解意為:標註在某個類上說明這個類是SpringBoot的主配置類,SpringBoot就應該執行這個類的main方法來啟動SpringBoot應用;是我們研究的重點!!!

@SpringBootApplication
public class Springboot01HelloworldQuick03Application {

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

接下來的分析由概括到詳細,由淺到深。

先了解大概,再逐一詳細分析

點進@SpringBootApplication,發現是一個組合註解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration	//表示這是一個springboot的配置類
@EnableAutoConfiguration	//開啟自動配置功能
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    ...
}

最重要兩個註解就是@SpringBootConfiguration@EnableAutoConfiguration

先看@SpringBootConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration	
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

再看@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage	//自動導包
@Import(AutoConfigurationImportSelector.class)	//自動配置匯入選擇
public @interface EnableAutoConfiguration {
    ...
}

大致研究的方向:

@SpringBootConfiguration:

springboot的配置類,標註在某個類上,表示這是一個springboot的配置類

再點進去看一看:

@Configuration 標註在某個類上,表示這是一個springboot的配置類

@Configuration:

這個註解我們比較熟悉,spring中的配置類,相當於之前的xml配置檔案

可以給容器中注入元件。。。以前配置檔案的功能都可以做

此註解 @Configuration 也可以點進去,發現也是一個元件

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    String value() default "";
}

這個簡單不做深究,後面為重點

@EnableAutoConfiguration:

顧名思義:開啟自動配置功能,這個註解一定和自動配置相關,點進去看一下

@AutoConfigurationPackage	//自動導包
@Import(AutoConfigurationImportSelector.class)	//自動配置匯入選擇

這兩個註解是我們研究的重點

@AutoConfigurationPackage:

點進此註解看一下

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
    ...
}

@Import為spring的註解,匯入一個配置檔案,在springboot中為給容器匯入一個元件

匯入的元件由AutoConfigurationPackages.Registrar.class執行邏輯來決定

再次點進去看看Registrar.class

並且可以打個斷點,啟動springboot,觀察一下

然後可以計算一下new PackageImports(metadata).getPackageNames()的值,

選中,右鍵

計算結果為com.liqiliang.springboot:

new PackageImport(metadata).getPackageName()最後計算這個包名為com.liqiliang.springboot

所以可以得到如下結論:

@AutoConfigurationPackage這個註解本身的含義就是將主配置類(@SpringBootApplication標註的類)所在的包下面所有的元件都掃描到spring容器中,包名都是主配置類的

所以如果將HelloController放到com.liqiliang.springboot包外面就掃描不到了,就會報錯

@EnableAutoConfiguration的另一個註解:

@Import(AutoConfigurationImportSelector.class) //自動配置匯入選擇

AutoConfigurationImportSelector 開啟自動配置類的導包的選擇器(匯入哪些元件的選擇器)

點進去看一下

這個類中存在方法可以幫我們獲取所有的配置

先做演示,再詳解

此位置打斷點,為了獲取configurations的值

然後重啟應用

一直往下執行,注意控制檯是否出現configurations的值,

點開configurations,陣列長度為127

注意看檔名,字尾全為 ***AutoConfiguration

將所有需要匯入的元件以全類名的方式返回,並新增到容器中

最終會給容器中匯入非常多的自動配置類(xxxAutoConfiguration),給容器中匯入這個場景需要的所有元件,並配置好這些元件

有了自動配置類,免去了我們手動編寫配置注入功能元件等工作

詳解:

所有的配置都存放在configurations中,

而這些配置都從getCandidateConfigurations方法中獲取,

這個getCandidateConfigurations方法是用來獲取候選的配置。

這個方法可以用來獲取所有候選的配置,那麼這些候選的配置又是從哪來的呢?

點進getCandidateConfigurations方法

getCandidateConfiguration方法返回的list,這個list其實是由loadFactoryNames返回的,loadFactoryNames方法執行的時候傳入了兩個引數,一個引數為getSpringFactoriesLoaderFactoryClass(),此引數是什麼?看下圖

然後看到了一個眼熟的詞 --->EnableAutoConfiguration

再去loadFactoryNames方法中看看,看他帶了EnableAutoConfiguration.class這個值做了什麼壞事情

先是將EnableAutoConfiguration.class傳給了factoryType,然後.getName( ),所以factoryTypeName值為EnableAutoConfiguration

然後呼叫loadSpringFactories( )方法,並且從類路徑下得到一個資源

FACTORIES_RESOURCE_LOCATION值為什麼?

前面成員位置定義好了

再回頭看看loadFactoryNames方法的下面有一些話

如下位置 還有一些話,是一條斷言

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."

意思是:configurations必須非空,否則就列印一段話,No auto configuration classes found in META-INF/spring.factories

所以我們有必要去找找這個檔案看看

(這個檔案記好了,整個J2EE的自動配置元件都在springboot-autoconfigure的jar包中

開啟之後會發現裡面包含了很多自動配置屬性

比如點開webmvc的

然後會看到很多關於mvc的配置

那麼掃描到的這些檔案作用是什麼呢:是把這個檔案的urls拿到之後並把這些urls每一個遍歷,最終把這些檔案整成一個properties物件,繼續看loadSpringFactories方法

然後從properties物件裡邊獲取一些值,把這些獲取到的值來載入我們最終要返回的這個結果

這個結果result為map集合,然後返回到loadFactoryNames方法中

這個factoryTypeName值為EnableAutoConfiguration

因為loadFactoryNames方法攜帶過來的第一個引數為EnableAutoConfiguration.class,所以factoryType值也為EnableAutoConfiguration.class,那麼factoryTypeName值為EnableAutoConfiguration

那麼map集合中getOrDefault方法為什麼意思呢?意思就是當Map集合中有這個key時,就使用這個key值,如果沒有就使用預設值defaultValue(第二個引數),所以是判斷是否包含EnableAutoConfiguration

看下圖,這不是包含著嘛

所以不得而知了,意為把spring-boot-autoconfigure-2.3.2.RELEASE.jar!/META-INF/spring.factories

這個檔案下,EnableAutoConfiguration下面所有的元件,每一個xxxAutoConfiguration類都是容器中的一個組

件,都加入到容器中。

加入到容器中之後的作用就是用它們來做自動配置,這就是Springboot自動配置之源,也就是自動配置的開始,

只有這些自動配置類進入到容器中以後,接下來這個自動配置類才開始進行啟動

那spring.factories中存在那麼多的配置,每次啟動時都是把它們全部載入嗎?神經病吧,不現實的

我們隨便點開一個類

再點開一個

是不是都有這個@ConditionalOnXXX註解,每一個類都有

@Conditional是spring底層註解,意思就是根據不同的條件,來進行自己不同的條件判斷,如果滿足指定的條件,那麼整個配置類裡邊的配置才會生效。

所以又不得而知了,在載入自動配置類的時候,並不是將spring.factories的配置全量載入進來,而是通過這個註解的判斷,如果註解中的類都存在,才會進行載入。

這樣就實現了springboot的自動配置

小結一下:

從網上貼的流程:

SpringBoot在啟動的時候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值

將這些值作為自動配置類匯入容器 , 自動配置類就生效 , 幫我們進行自動配置工作;

以前我們需要自己配置的東西 , 自動配置類都幫我們解決了

整個J2EE的整體解決方案和自動配置都在springboot-autoconfigure的jar包中;

它將所有需要匯入的元件以全類名的方式返回 , 這些元件就會被新增到容器中 ;

它會給容器中匯入非常多的自動配置類 (xxxAutoConfiguration), 就是給容器中匯入這個場景需要的所有元件 , 並配置好這些元件 ;

有了自動配置類 , 免去了我們手動編寫配置注入功能元件等的工作;

本人剛接觸springboot,草草的做了這麼一個自動配置原理分析。有些表述不清楚或錯誤的地方請見諒。

最後說一句,springboot流弊!!!