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-starter
和web
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流弊!!!