最通俗易懂,最詳細的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), 就是給容器中導入這個場景需要的所有組件 , 並配置好這些組件 ;

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



簡要概括一下自動裝備:

啟動類中有一個@SpringBootApplication注解,包含了@EnableAutoConfiguration代表開啟自動

裝配,@EnableAutoConfiguration注解會去spring-boot-autoconfigure工程下尋找 META-

INF/spring.factories 文件,這個文件中列舉了所有自動裝備類的清單,有一百多個,然后自動讀取里

面的自動裝配配置類清單。

但是這些類不會全部加載,很顯然這樣非常不合理。但是因為有@ConditionalOn條件注解,滿足

一定條件配置才會生效 如: @ConditionalOnClass(某類.class) 工程中必須包含一些相關的類時,

配置才會生效。所以說當我們的起步依賴中引入了一些對應的類之后,自動裝備的條件滿足了,自

動裝備才會被觸發。



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

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


免責聲明!

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



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