主程序(重要)
//@SpringBootApplication 標注,是一個SpringBoot應用
@SpringBootApplication
public class SpringbootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
再寫SpringBoot項目的時候,總要寫這么一個主程序,這個主程序最大的特點就是其類上放了一個@SpringBootApplication注解,這也正是SpringBoot項目啟動的核心,也是我們要研究的重點。
注意:之后的分析可能會深入源碼,源碼是一層一層嵌套的,所以光靠文字說明會比較難以理解,最好是自己在IDE環境下跟著一步一步跟著點下去。當然也可以繞過這一部分直接看結論。
點開@SpringBootApplication,可以發現它是一個組合注解,主要是由這么幾個注解構成的。
@SpringBootConfiguration//核心
@EnableAutoConfiguration//核心
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
我們首先要研究的就是核心的兩個注解 @SpringBootConfiguration和 @EnableAutoConfiguration ,逐個進行分析。
@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
可以看到SpringBootConfiguration其實就攜帶了一個@Configuration注解,這個注解我們再熟悉不過了,他就代表自己是一個Spring的配置類。所以我們可以認為:@SpringBootConfiguration = @Configuration
@EnableAutoConfiguration
顧名思義,這個注解一定和自動配置相關,點進去看源代碼之后可以發現,其內部就包含了這么兩個注解。
@AutoConfigurationPackage //自動配置包
@Import(AutoConfigurationImportSelector.class)//自動配置導入選擇
來看看@Import(AutoConfigurationImportSelector.class)
中的內容:
它幫我們導入了AutoConfigurationImportSelector,這個類中存在一個方法可以幫我們獲取所有的配置,代碼如下。
/*
所有的配置都存放在configurations中,
而這些配置都從getCandidateConfiguration中獲取,
這個方法是用來獲取候選的配置。
*/
List
getCandidateConfigurations():
這個方法可以用來獲取所有候選的配置,那么這些候選的配置又是從哪來的呢?
/*獲取候選的配置*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
實際上它返回了一個List,這個List是由 loadFactoryNames() 方法返回的,其中傳入了一個getSpringFactoriesLoaderFactoryClass(),我們可以看看這個方法的內容。
protected Class? getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
我們看到了一個眼熟的詞 —— EnableAutoConfiguration ,也就是說,它實際上返回的就是標注了這個類的所有包。標注了這個類的包不就是@SpringBootApplication
嗎?
所以我們可以得出結論:它兜兜轉轉繞了這么多地方,就是為了將啟動類所需的所有資源導入。
我們接著往下看,它其中還有這么一條語句,是一條斷言:
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
,我們把這個邏輯反過來想想。如果這個集合不為空,是不是就代表找到了這個spring.factories并且會去加載這個文件中的內容呢?
帶著這個疑問,我們首先找到spring.factories這個文件:
可以看到里面包含了很多自動配置屬性:
我們可以隨便找一個自動配置點進去,比如WebMvcAutoConfiguration
:
這里放了所有關于WebMvc的配置,如視圖解析器、國際化等等。
分析到這里,我們就可以得出一個完整的結論了:
當我們的SpringBoot項目啟動的時候,會先導入AutoConfigurationImportSelector,這個類會幫我們選擇所有候選的配置,我們需要導入的配置都是SpringBoot幫我們寫好的一個一個的配置類,那么這些配置類的位置,存在與META-INF/spring.factories文件中,通過這個文件,Spring可以找到這些配置類的位置,于是去加載其中的配置。
看到這里,可能有些同學會存在疑問,spring.factories中存在那么多的配置,每次啟動時都是把它們全量加載嗎?這顯然是不現實的。
這其實也是我在看源碼的時候存在疑問的地方,因為其中有一個注解并不常用,我們點開一個配置類就可以看到。
@ConditionalOnXXX:如果其中的條件都滿足,該類才會生效。
所以在加載自動配置類的時候,并不是將spring.factories的配置全量加載進來,而是通過這個注解的判斷,如果注解中的類都存在,才會進行加載。
所以就實現了:我們在pom.xml文件中加入stater啟動器,SpringBoot自動進行配置。完成開箱即用。
結論
SpringBoot所有自動配置類都是在啟動的時候進行掃描并加載,通過spring.factories可以找到自動配置類的路徑,但是不是所有存在于spring,factories中的配置都進行加載,而是通過@ConditionalOnClass注解進行判斷條件是否成立(只要導入相應的stater,條件就能成立),如果條件成立則加載配置類,否則不加載該配置類。
在這里貼一個我認為的比較容易理解的過程:
- SpringBoot在啟動的時候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值
- 將這些值作為自動配置類導入容器 , 自動配置類就生效 , 幫我們進行自動配置工作;
- 以前我們需要自己配置的東西 , 自動配置類都幫我們解決了
- 整個J2EE的整體解決方案和自動配置都在springboot-autoconfigure的jar包中;
- 它將所有需要導入的組件以全類名的方式返回 , 這些組件就會被添加到容器中 ;
- 它會給容器中導入非常多的自動配置類 (xxxAutoConfiguration), 就是給容器中導入這個場景需要的所有組件 , 并配置好這些組件 ;
- 有了自動配置類 , 免去了我們手動編寫配置注入功能組件等的工作;
摘自https://blog.kuangstudy.com/index.php/archives/630/
約定大于配置
開箱即用的原理說完了,約定大于配置就比較好理解了。其實約定大于配置就是開箱即用中那些自動配置的細節。說的具體點就是: 我們的配置文件(.yml)應該放在哪個目錄下,配置文件的命名規范,項目啟動時掃描的Bean,組件的默認配置是什么樣的(比如SpringMVC的視圖解析器)等等等等這一系列的東西,都可以被稱為約定 ,下面就來一點一點地說一下SpringBoot中的“約定”。
maven目錄結構的約定
我們可以去Spring的官網查看一下官方文檔,看看文檔中描述的目錄結構是怎樣的。
Config locations are searched in reverse order. By default, the configured locations are classpath:/,classpath:/config/,file:./,file:./config/. The resulting search order is the following:
- file:./config/
- file:./
- classpath:/config/
- classpath:/
也就是說,spring的配置文件目錄可以放在
- /config
- /(根目錄)
- resource/config/
- resource/
這四個路徑從上到下存在優先級關系。
SpringBoot默認配置文件的約定
SpringBoot默認可以加載以下三種配置文件:
- application.yml
- application.yaml
- application.properties
建議使用前兩種作為項目的配置文件。
項目啟動時掃描包范圍的約定
SpringBoot的注解掃描的默認規則是SpringBoot的入口類所在包及其子包。
若入口類所在的包是cn.objectspace.demo那么自動掃描包的范圍是cn.objectspace.demo包及其下面的子包,如果service包和dao包不在此范圍,則不會自動掃描。
SpringBoot自動配置類如何讀取yml配置
從更細節的角度去理解自動配置
上文中我們闡述了一些SpringBoot自動配置的原理,我們是從全局的角度去看自動配置的整個過程。比如從哪個地方開始進行裝配流程、如何找到裝配的包等。
那么現在將自己的視角貼近SpringBoot,來聊聊application.yml中我們配置的東西,是如何配置到一個個的配置類中的。
yml配置文件中可以配置那些東西
首先要知道這個問題的答案,我們應該習慣springboot的配置方式。在上文中我們闡述了SpringBoot總是將所有的配置都用JavaConfig的形式去呈現出來,這樣能夠使代碼更加優雅。
那么yml中配置的東西,必然是要和這種配置模式去進行聯系的,我們在application.yml中配置的東西,通常是一些存在與自動配置類中的屬性,那么這些自動配置類,在啟動的時候是怎么找到的呢?
如果你還記得上文的描述,那么你可以很明確地知道:spring.factories!沒錯,就是它,所以這個問題我們似乎得到了答案——只要存在與spring.factories中的,我們都可以在application.yml中進行配置。
當然,這并不意味著不存在其中的我們就不能配置,這些配置類我們是可以進行自定義的,只要我們寫了配置類,我們就可以在yml中配置我們需要的屬性值,然后在配置類中直接讀取這個配置文件,將其映射到配置類的屬性上。那么就牽扯出我們的問題了:配置類是如何去讀取yml配置文件中的信息的呢?
@ConfigurationProperties
要明白這個問題。我們就首先要去了解這個注解有什么作用。
我們可以自己嘗試在application.yml中去定義一些屬性,如下:
object:
name: Object
blogurl: blog.objectspace.cn
我們現在自己定義一個類去讀取這個文件:
@Component
@ConfigurationProperties(prefix = "object")
public class TestConfig {
private String name;
private String blogUrl;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBlogUrl() {
return blogUrl;
}
public void setBlogUrl(String blogUrl) {
this.blogUrl = blogUrl;
}
}
然后我們在測試類中輸出一下這個對象:
@SpringBootTest
class SpringbootdemoApplicationTests {
@Autowired
TestConfig testConfig;
@Test
void contextLoads() {
System.out.println(testConfig.getName());
System.out.println(testConfig.getBlogUrl());
}
}
測試結果:
我們可以看到,在控制臺中輸出了我們在yml中配置的屬性值,但是這些值我們沒有在任何地方顯式地對這個對象進行注入。
所以@ConfigurationProperties這個注解,可以將yml文件中寫好的值注入到我們類的屬性中。
明白了它的作用,就能明白自動配置類工作的原理了。
我們依舊是選取SpringMVC的自動配置類,我們來看看其中有些什么東西。
點擊任意一個*Properties類中,look一下其中的內容:
看到這里相信所有人都明白了,我們就拿mvc配置來舉例。
我們在yml中配置的date-format,就可以通過@ConfigurationProperties映射到類中的dateFormat中,然后在通過自動配置類,將這些屬性配置到配置類中。
-
JAVA
+關注
關注
19文章
2967瀏覽量
104746 -
開發
+關注
關注
0文章
370瀏覽量
40842 -
框架
+關注
關注
0文章
403瀏覽量
17483 -
spring
+關注
關注
0文章
340瀏覽量
14343
發布評論請先 登錄
相關推薦
評論