當前位置:首頁 » 操作系統 » springboot源碼分析

springboot源碼分析

發布時間: 2023-02-06 21:31:14

1. Spring全家桶筆記:Spring+Spring Boot+Spring Cloud+Spring MVC

最近我整理了一下一線架構師的Spring全家桶筆記:Spring+Spring Boot+Spring Cloud+Spring MVC,分享給大家一起學習一下~ 文末免費獲取哦

Spring是一個輕量級控制反轉(IoC)和面向切面(AOP)的容器框架。Spring框架是由於軟體開發的復雜性而創建的。Spring使用的是基本的javaBean來完成以前只可能由EJB完成的事情。然而,Spring的用途不僅僅限於伺服器端的開發。從簡單性、可測試性和松耦合性角度而言,絕大部分Java應用都可以從Spring中受益。

1.1 Spring面試必備題+解析

1.2 Spring學習筆記

(1)Spring源碼深入解析

(2)Spring實戰

1.3 Spring學習思維腦圖

Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。通過這種方式,Spring Boot致力於在蓬勃發展的快速應用開發領域(rapid application development)成為領導者。

2.1 Spring Boot面試必備題+解析

2.2 Spring Boot學習筆記

(1)Spring Boot實踐

(2)SpringBoot揭秘 快速構建微服務體系

2.3 SpringBoot學習思維腦圖

springcloud是微服務架構的集大成者,將一系列優秀的組件進行了整合。基於springboot構建,對我們熟悉spring的程序員來說,上手比較容易。通過一些簡單的註解,我們就可以快速的在應用中配置一下常用模塊並構建龐大的分布式系統。

3.1 Spring Cloud面試必備題+解析

3.2 Spring Cloud學習筆記

(1)Spring Cloud參考指南

SpringMVC是一種基於Java的實現MVC設計模式的請求驅動類型的輕量級Web框架,使用了MVC架構模式的思想,將web層進行職責解耦,基於請求驅動指的就是使用請求-響應模型,框架的目的就是幫助我們簡化開發

4.1 Spring MVC面試必備題+解析

4.2 Spring MVC學習筆記

(1)看透Spring MVC源代碼分析與實踐

(2)精通Spring MVC

最後分享一下一份JAVA核心知識點整理(PDF)

2. [Spring boot源碼解析] 2 啟動流程分析

在了解 Spring Boot 的啟動流程的時候,我們先看一下一個Spring Boot 應用是如何啟動的,如下是一個簡單的 SpringBoot 程序,非常的簡潔,他是如何做到的呢,我們接下來就將一步步分解。

我們追蹤 SpringApplication.run() 方法,其實最終它主要的邏輯是新建一個 SpringApplication ,然後調用他的 run 方法,如下:

我們先來看一下創建 SpringApplication 的方法:

在將Main class 設置 primarySources 後,調用了 WebApplicationType.deceFromClasspath() 方法,該方法是為了檢查當前的應用類型,並設置給 webApplicationType 。 我們進入 deceFromClasspath 方法 :

這里主要是通過類載入器判斷是否存在 REACTIVE 相關的類信息,假如有就代表是一個 REACTIVE 的應用,假如不是就檢查是否存在 Servelt 和 ,假如都沒有,就代表應用為非 WEB 類應用,返回 NONE ,默認返回 SERVLET 類型,我們這期以我們目前最常使用的 SERVLET 類型進行講解,所以我們在應用中引入了 spring-boot-starter-web 作為依賴:

他會包含 Spring-mvc 的依賴,所以就包含了內嵌 tomcat 中的 Servlet 和 Spring-web 中的 ,因此返回了 SERVLET 類型。

回到剛才創建 SpringApplication 的構建方法中,我們設置完成應用類型後,就尋找所有的 Initializer 實現類,並設置到 SpringApplication 的 Initializers 中,這里先說一下 getSpringFactoriesInstances 方法,我們知道在我們使用 SpringBoot 程序中,會經常在 META-INF/spring.factories 目錄下看到一些 EnableAutoConfiguration ,來出發 config 類注入到容器中,我們知道一般一個 config 類要想被 SpringBoot 掃描到需要使用 @CompnentScan 來掃描具體的路徑,對於 jar 包來說這無疑是非常不方便的,所以 SpringBoot 提供了另外一種方式來實現,就是使用 spring.factories ,比如下面這個,我們從 Springboot-test 中找到的例子,這里先定義了一個ExampleAutoConfiguration,並加上了 Configuration 註解:

然後在 spring.factories 中定義如下:

那這種方式是怎麼實現的你,這就要回到我們剛才的方法 getSpringFactoriesInstances :

我們先來看一下傳入參數,這里需要注意的是 args,這個是初始化對應 type 的時候傳入的構造參數,我們先看一下 SpringFactoriesLoader#loadFactoryNames 方法:

首先是會先檢查緩存,假如緩存中存在就直接返回,假如沒有就調用 classLoader#getResources 方法,傳入 META-INF/spring.factories ,即獲取所有 jar 包下的對應文件,並封裝成 UrlResource ,然後使用 PropertiesLoaderUtils 將這些信息讀取成一個對一對的 properties,我們觀察一下 spring.factories 都是按 properties 格式排版的,假如有多個就用逗號隔開,所以這里還需要將逗號的多個類分隔開來,並加到 result 中,由於 result 是一個 LinkedMultiValueMap 類型,支持多個值插入,最後放回緩存中。最終完成載入 META-INF/spring.factories 中的配置,如下:

我們可以看一下我們找到的 initializer 有多少個:

在獲取到所有的 Initializer 後接下來是調用 方法進行初始化。

這里的 names 就是我們上面通過類載入器載入到的類名,到這里會先通過反射生成 class 對象,然後判斷該類是否繼承與 ApplicationContextInitializer ,最後通過發射的方式獲取這個類的構造方法,並調用該構造方法,傳入已經定義好的構造參數,對於 ApplicationContextInitializer 是無參的構造方法,然後初始化實例並返回,回到原來的方法,這里會先對所有的 ApplicationContextInitializer 進行排序,調用 #sort(instances) 方法,這里就是根據 @Order 中的順序進行排序。

接下來是設置 ApplicationListener ,我們跟進去就會發現這里和上面獲取 ApplicationContextInitializer 的方法如出一轍,最終會載入到如圖的 15 個 listener (這里除了 外,其他都是 SpringBoot 內部的 Listener):

在完成 SpringApplication 對象的初始化後,我們進入了他的 run 方法,這個方法幾乎涵蓋了 SpringBoot 生命周期的所有內容,主要分為九個步驟,每一個步驟這里都使用註解進行標識:

主要步驟如下:
第一步:獲取 SpringApplicationRunListener, 然後調用他的 staring 方法啟動監聽器。
第二步:根據 SpringApplicationRunListeners以及參數來准備環境。
第三步:創建 Spring 容器。
第四步:Spring 容器的前置處理。
第五步:刷新 Spring 容器。
第六步: Spring 容器的後置處理器。
第七步:通知所有 listener 結束啟動。
第八步:調用所有 runner 的 run 方法。
第九步:通知所有 listener running 事件。
我們接下來一一講解這些內容。

我們首先看一下第一步,獲取 SpringApplicationRunListener :

這里和上面獲取 initializer 和 listener 的方式基本一致,都是通過 getSpringFactoriesInstances , 最終只找到一個類就是: org.springframework.boot.context.event.EventPublishingRunListener ,然後調用其構造方法並傳入產生 args , 和 SpringApplication 本身:

我們先看一下構造函數,首先將我們獲取到的 ApplicationListener 集合添加到initialMulticaster 中, 最後都是通過操作 來進行廣播,我,他繼承於 ,我們先看一下他的 addApplicationListener 方法:

我們可以看出,最後是放到了 applicationListenters 這個容器中。他是 defaultRetriever 的成員屬性, defaultRetriever 則是 的私有類,我們簡單看一下這個類:

我們只需要看一下這里的 getApplicationListeners 方法,它主要是到 beanFactory 中檢查是否存在多的 ApplicationListener 和舊的 applicationListeners 組合並返回,接著執行 listener 的 start 方法,最後也是調用了 的 multicastEvent 查找支持對應的 ApplicationEvent 類型的通知的 ApplicationListener 的 onApplicationEvent 方法 ,這里除了會:

篩選的方法如下,都是調用了對應類型的 supportsEventType 方法 :

如圖,我們可以看到對 org.springframework.boot.context.event.ApplicationStartingEvent 感興趣的有5個 Listener

環境准備的具體方法如下:

首先是調用 getOrCreateEnvironment 方法來創建 environment ,我們跟進去可以發現這里是根據我們上面設置的環境的類型來進行選擇的,當前環境會創建 StandardServletEnvironment

我們先來看一下 StandardServletEnvironment 的類繼承關系圖,我們可以看出他是繼承了 AbstractEnvironment :

他會調用子類的 customizePropertySources 方法實現,首先是 StandardServletEnvironment 的實現如下,他會添加 servletConfigInitParams , servletContextInitParams , jndiProperties 三種 properties,當前調試環境沒有配置 jndi properties,所以這里不會添加。接著調用父類的 customizePropertySources 方法,即調用到了 StandardEnvironment 。

我們看一下 StandardEnvironment#customizePropertySources 方法,與上面的三個 properties 創建不同,這兩個是會進行賦值的,包括系統環境變數放入 systemEnvironment 中,jvm 先關參數放到 systemProperties 中:

這里會添加 systemEnvironment 和 systemProperties 這兩個 properties,最終拿到的 properties 數量如下 4個:

在創建完成 Environment 後,接下來就到了調用 configureEnvironment 方法:

我們先看一下 configurePropertySources 方法,這里主要分兩部分,首先是查詢當前是否存在 defaultProperties ,假如不為空就會添加到 environment 的 propertySources 中,接著是處理命令行參數,將命令行參數作為一個 CompositePropertySource 或則 添加到 environment 的 propertySources 裡面,

接著調用 ConfigurationPropertySources#attach 方法,他會先去 environment 中查找 configurationProperties , 假如尋找到了,先檢查 configurationProperties 和當前 environment 是否匹配,假如不相等,就先去除,最後添加 configurationProperties 並將其 sources 屬性設置進去。

回到我們的 prepareEnvironment 邏輯,下一步是通知觀察者,發送 事件,調用的是 SpringApplicationRunListeners#environmentPrepared 方法,最終回到了 #multicastEvent 方法,我們通過 debug 找到最後對這個時間感興趣的 Listener 如下:

其主要邏輯如下:

這個方法最後載入了 PropertySourceLoader , 這里主要是兩種,一個是用於 Properties 的,一個是用於 YAML 的如下:

其中 apply 方法主要是載入 defaultProperties ,假如已經存在,就進行替換,而替換的目標 PropertySource 就是 load 這里最後的一個 consumer 函數載入出來的,這里列一下主要做的事情:
1、載入系統中設置的所有的 Profile 。
2、遍歷所有的 Profile ,假如是默認的 Profile , 就將這個 Profile 加到 environment 中。
3、調用load 方法,載入配置,我們深入看一下這個方法:

他會先調用 getSearchLocations 方法,載入所有的需要載入的路徑,最終有如下路徑:

其核心方法是遍歷所有的 propertySourceLoader ,也就是上面載入到兩種 propertySourceLoader ,最紅 loadForFileExtension 方法,載入配置文件,這里就不展開分析了,說一下主要的作用,因為每個 propertySourceLoader 都有自己可以載入的擴展名,默認擴展名有如下四個 properties, xml, yml, yaml,所以最終拿到文件名字,然後通過 - 拼接所有的真實的名字,然後加上路徑一起載入。

接下來,我們分析 BackgroundPreinitializer ,這個方法在接收 ApplicationPrepareEnvironment 事件的時候真正調用了這份方法:

1、 ConversionServiceInitializer 主要負責將包括 日期,貨幣等一些默認的轉換器注冊到 formatterRegistry 中。
2、 ValidationInitializer 創建 validation 的匹配器。
3、 MessageConverterInitializer 主要是添加了一些 http 的 Message Converter。
4、 JacksonInitializer 主要用於生成 xml 轉換器的。
接著回到我們將的主體方法, prepareEnvironment 在調用完成 listeners.environmentPrepared(environment) 方法後,調用 bindToSpringApplication(environment) 方法,將 environment 綁定到 SpirngApplication 中。
接著將 enviroment 轉化為 StandardEnvironment 對象。
最後將 configurationProperties 加入到 enviroment 中, configurationProperties 其實是將 environment 中其他的 PropertySource 重新包裝了一遍,並放到 environment 中,這里主要的作用是方便 進行解析。

它主要是檢查是否存在 spring.beaninfo.ignore 配置,這個配置的主要作用是設置 javaBean 的內省模式,所謂內省就是應用程序在 Runtime 的時候能檢查對象類型的能力,通常也可以稱作運行時類型檢查,區別於反射主要用於修改類屬性,內省主要用戶獲取類屬性。那麼我們什麼時候會使用到內省呢,java主要是通過內省工具 Introspector 來完成內省的工作,內省的結果通過一個 Beaninfo 對象返回,主要包括類的一些相關信息,而在 Spring中,主要是 BeanUtils#Properties 會使用到,Spring 對內省機制還進行了改進,有三種內省模式,如下圖中紅色框框的內容,默認情況下是使用 USE_ALL_BEANINFO。假如設置為true,就是改成第三中 IGNORE_ALL_BEANINFO

首先是檢查 Application的類型,然後獲取對應的 ApplicationContext 類,我們這里是獲取到了 org.springframework.boot.web.servlet.context. 接著調用 BeanUtils.instantiateClass(contextClass); 方法進行對象的初始化。

最終其實是調用了 的默認構造方法。我們看一下這個方法做了什麼事情。這里只是簡單的設置了一個 reader 和一個 scanner,作用於 bean 的掃描工作。

我們再來看一下這個類的繼承關系

這里獲取 ExceptionReporter 的方式主要還是和之前 Listener 的方式一致,通過 getSpringFactoriesInstances 來獲取所有的 SpringBootExceptionReporter 。

其主要方法執行如下:

3. SpringBoot內置生命周期事件詳解 SpringBoot源碼(十)

SpringBoot中文注釋項目Github地址:

https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE

本篇接 SpringBoot事件監聽機制源碼分析(上) SpringBoot源碼(九)

溫故而知新,我們來簡單回顧一下上篇的內容,上一篇我們分析了 SpringBoot啟動時廣播生命周期事件的原理 ,現將關鍵步驟再濃縮總結下:

上篇文章的側重點是分析了SpringBoot啟動時廣播生命周期事件的原理,此篇文章我們再來詳細分析SpringBoot內置的7種生命周期事件的源碼。

分析SpringBoot的生命周期事件,我們先來看一張類結構圖:

由上圖可以看到事件類之間的關系:

EventObject 類是JDK的事件基類,可以說是所有Java事件類的基本,即所有的Java事件類都直接或間接繼承於該類,源碼如下:

可以看到 EventObject 類只有一個屬性 source ,這個屬性是用來記錄最初事件是發生在哪個類,舉個栗子,比如在SpringBoot啟動過程中會發射 ApplicationStartingEvent 事件,而這個事件最初是在 SpringApplication 類中發射的,因此 source 就是 SpringApplication 對象。

ApplicationEvent 繼承了DK的事件基類 EventObject 類,是Spring的事件基類,被所有Spring的具體事件類繼承,源碼如下:

可以看到 ApplicationEvent 有且僅有一個屬性 timestamp ,該屬性是用來記錄事件發生的時間。

SpringApplicationEvent 類繼承了Spring的事件基類 ApplicationEvent ,是所有SpringBoot內置生命周期事件的父類,源碼如下:

可以看到 SpringApplicationEvent 有且僅有一個屬性 args ,該屬性就是SpringBoot啟動時的命令行參數即標注 @SpringBootApplication 啟動類中 main 函數的參數。

接下來我們再來看一下 SpringBoot 內置生命周期事件即 SpringApplicationEvent 的具體子類們。

SpringBoot開始啟動時便會發布 ApplicationStartingEvent 事件,其發布時機在環境變數Environment或容器ApplicationContext創建前但在注冊 ApplicationListener 具體監聽器之後,標志標志 SpringApplication 開始啟動。

可以看到 事件多了一個 environment 屬性,我們不妨想一下,多了 environment 屬性的作用是啥?
答案就是 事件的 environment 屬性作用是利用事件發布訂閱機制,相應監聽器們可以從 事件中取出 environment 變數,然後我們可以為 environment 屬性增加屬性值或讀出 environment 變數中的值。

當SpringApplication已經開始啟動且環境變數 Environment 已經創建後,並且為環境變數 Environment 配置了命令行和 Servlet 等類型的環境變數後,此時會發布 事件。

監聽 事件的第一個監聽器是 ConfigFileApplicationListener ,因為是 ConfigFileApplicationListener 監聽器還要為環境變數 Environment 增加 application.properties 配置文件中的環境變數;此後還有一些也是監聽 事件的其他監聽器監聽到此事件時,此時可以說環境變數 Environment 幾乎已經完全准備好了。

可以看到 事件多了個 類型的 context 屬性, context 屬性的作用同樣是為了相應監聽器可以拿到這個 context 屬性執行一些邏輯,具體作用將在 3.4.4 詳述。

事件在 ApplicationContext 容器創建後,且為 ApplicationContext 容器設置了 environment 變數和執行了 的初始化方法後但在bean定義載入前觸發,標志ApplicationContext已經初始化完畢。

同樣可以看到 ApplicationPreparedEvent 事件多了個 類型的 context 屬性,多了 context 屬性的作用是能讓監聽該事件的監聽器們能拿到 context 屬性,監聽器拿到 context 屬性一般有如下作用:

ApplicationPreparedEvent 事件在 ApplicationContext 容器已經完全准備好時但在容器刷新前觸發,在這個階段 bean 定義已經載入完畢還有 environment 已經准備好可以用了。

ApplicationStartedEvent 事件將在容器刷新後但 ApplicationRunner 和 CommandLineRunner 的 run 方法執行前觸發,標志 Spring 容器已經刷新,此時容器已經准備完畢了。

ApplicationReadyEvent 事件在調用完 ApplicationRunner 和 CommandLineRunner 的 run 方法後觸發,此時標志 SpringApplication 已經正在運行。

可以看到 ApplicationFailedEvent 事件除了多了一個 context 屬性外,還多了一個 Throwable 類型的 exception 屬性用來記錄SpringBoot啟動失敗時的異常。

ApplicationFailedEvent 事件在SpringBoot啟動失敗時觸發,標志SpringBoot啟動失敗。

此篇文章相對簡單,對SpringBoot內置的7種生命周期事件進行了詳細分析。我們還是引用上篇文章的一張圖來回顧一下這些生命周期事件及其用途:

由於有一些小夥伴們建議之前有些源碼分析文章太長,導致耐心不夠,看不下去,因此,之後的源碼分析文章如果太長的話,筆者將會考慮拆分為幾篇文章,這樣就比較短小了,比較容易看完,嘿嘿。

【源碼筆記】Github地址:

https://github.com/yuanmabiji/Java-SourceCode-Blogs

點贊搞起來,嘿嘿嘿!

公眾號【 源碼筆記 】專注於Java後端系列框架的源碼分析。

4. Springboot初始化流程解析

以上是一個最簡單的Springboot程序(2.0.3版本)示例,也是我們最通用的寫法,但其中其實封裝這一系列復雜的功能操作,讓我們開始逐步進行分析。

首先這里最重要的必然是註解 @SpringBootApplication

@SpringBootApplication 註解由幾個註解復合組成,其中最主要的就是 @SpringBootConfiguration 、 @EnableAutoConfiguration 和 @ComponentScan 這三個。

其中的 @ComponentScan 是spring的原生註解, @SpringBootConfiguration 雖然是springboot中的註解,但其實質就是包裝後的 @Configuration ,仍然是spring中的註解,用於代替xml的方式管理配置bean

@EnableAutoConfiguration 的定義如上,這里最重要的註解是 @Import ( @AutoConfigurationPackage 註解的實現也是基於 @Import ),藉助 @Import 的幫助,將所有符合自動配置條件的bean定義載入到IoC容器中。關於 @EnableAutoConfiguration 註解後續涉及到時會再詳細說明。這里我們先回到啟動類的 run 方法從頭分析初始化流程。

可以看到'run'方法最終調用的是 new SpringApplication(primarySources).run(args) ,這里首先創建了 SpringApplication 對象,然後調用其 run 方法

這里主要是為 SpringApplication 對象進行初始化,這里要專門提一下的是 webApplicationType 和 getSpringFactoriesInstances 。

它用來標識我們的應用是什麼類型的應用,來看一下 deceWebApplicationType() 方法的實現

其返回值是 WebApplicationType 類型的枚舉類,其值有 NONE 、 SERVLET 、 REACTIVE 三種,分別對應非WEB應用,基於servlet的WEB應用和基於reactive的WEB應用。

這里的核心是 SpringFactoriesLoader.loadFactoryNames(type, classLoader) 方法,來看一下

重點關注一下 loadSpringFactories(classLoader) 做了什麼

這里的 FACTORIES_RESOURCE_LOCATION 定義為 META-INF/spring.factories ,因此該方法會掃描所有包下的該文件,將其解析成map對象並緩存到 cache 中以避免重復載入,springboot包下該文件的部分片段如下

從這里可以看出, setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)) 和 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 分別對應設置的是上述這些類。

解析完成後調用 (type, parameterTypes, classLoader, args, names) 處理解析結果,生成對應的實例,源碼如下

這里的核心是通過 ClassUtils.forName(name, classLoader) 方法,以反射的方式生成類實例 instanceClass 。由此可以看出 SpringFactoriesLoader.loadFactoryNames(type, classLoader) 的作用就是將 META-INF/spring.factories 中配置的內容進行實例化的工廠方法類,具備很強的擴展性,與SPI機制有異曲同工
的效果。

看完 SpringApplication 的初始化,接著跳回 run 方法繼續分析

這里挑其中比較重要的幾個方法進行分析

通過 getOrCreateEnvironment() 方法創建容器環境

可以看到 environment 存在則不會重復創建,當應用類型為servlet時創建的是 StandardServletEnvironment 對象,否則創建 StandardEnvironment 對象。

接著來看 configureEnvironment(environment, applicationArguments.getSourceArgs())

configurePropertySources(environment, args) 載入啟動命令行的配置屬性,來看一下實現

這里的 MutablePropertySources 對象用於存儲配置集合,其內部維護了一個 CopyOnWriteArrayList 類型的list對象,當默認配置存在時,會向該list的尾部插入一個 new MapPropertySource("defaultProperties", this.defaultProperties) 對象。

接著來看 configureProfiles(environment, args)

這里主要做的事情就是獲取 environment.getActiveProfiles() 的參數設置到 environment 中,即 spring.profiles.active 對應的環境變數。

最後來看一下 listeners.environmentPrepared(environment)

這里的 listeners 就是之前通過 META-INF/spring.factories 注冊的所有listeners,後面我們先以其中最重要的 ConfigFileApplicationListener 做為例子進行分析,接著來看 listener.environmentPrepared(environment)

可以看到這里創建了一個 類型的事件,並且調用了 multicastEvent 方法,通過該方法最終會調用到listener的 onApplicationEvent 方法,觸發事件監聽器的執行。

接下來具體看一下 ConfigFileApplicationListener 的 onApplicationEvent 方法做了什麼

可以看到當監聽到 類型的事件時,調用 on( () event) 方法

可以看到這里通過 loadPostProcessors() 方法載入了 META-INF/spring.factories 中的所有 EnvironmentPostProcessor 類到list中,同時把 ConfigFileApplicationListener 自己也添加進去了。接著遍歷list中所有對象,並執行 postProcessEnvironment 方法,於是接著來看該方法

這里的核心是 new Loader(environment, resourceLoader).load() ,這里的 Loader 是一個內部類,用於處理配置文件的載入,首先看一下其構造方法

可以看到這里的 resourceLoader 又是通過 SpringFactoriesLoader 進行載入,那麼來看看 META-INF/spring.factories 中定義了哪些 resourceLoader

從名字就可以看出來, 和 YamlPropertySourceLoader 分別用於處理.properties和.yml類型的配置文件。

接著來看看 load() 方法做了什麼

initializeProfiles() 進行了 profiles 的初始化,默認會添加 null 和 default 到 profiles 中, null 對應配置文件application.properties和application.yml, default 對應配置文件application-default.yml和application-default.properties,這里的 null 會被優先處理,由於後處理的會覆蓋先處理的,因此其優先順序最低。

接著來看 load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false)) 方法

這里重點是通過 getSearchLocations() 獲取配置文件的路徑,默認會獲得4個路徑

接著會遍歷這些路徑,拼接配置文件名稱,選擇合適的yml或者properties解析器進行解析,最後將結果添加到 environment 的 propertySources 中。

可以看到這里也是根據 webApplicationType 的取值,分別創建不同的返回類型。

這里的 sources 裝的就是我們的啟動類,然後通過 load(context, sources.toArray(new Object[0])) 方法進行載入

來看一下 loader 是如何被載入的

經過一系列調用之後最終由 load(Class<?> source) 方法執行,這里比較有趣的是當Groovy存在時居然是優先調用Groovy的方式進行載入,否則才走 this.annotatedReader.register(source) 方法將啟動類注冊到 beanDefinitionMap 中。

這個 refresh() 方法相當重要,尤其是 (beanFactory) ,這是實現spring-boot-starter-*(mybatis、redis等)自動化配置的關鍵部分,後續再詳細講解。

至此Springboot的啟動流程已經大體分析完了,也了解了配置文件和啟動類分別是是如何被載入的,但仍有兩個問題待解,一是Springboot的核心思想約定大於配置是如何做到的,二是Springboot的各種spring-boot-starter-*是如何發揮作用的,這兩個問題留待後續文章繼續分析。

熱點內容
安卓小說怎麼導出 發布:2024-04-24 03:51:23 瀏覽:348
不用編譯安裝linux 發布:2024-04-24 03:50:00 瀏覽:630
希望之村體驗服如何進入伺服器 發布:2024-04-24 03:31:11 瀏覽:210
土地變更資料庫 發布:2024-04-24 03:14:52 瀏覽:238
備份sql的表 發布:2024-04-24 03:00:10 瀏覽:127
tls加密 發布:2024-04-24 02:59:36 瀏覽:520
篩選法的演算法 發布:2024-04-24 02:54:28 瀏覽:902
武漢大學編譯原理 發布:2024-04-24 02:25:47 瀏覽:298
自己搭建雲手機平台伺服器配置 發布:2024-04-24 02:00:40 瀏覽:164
澤拉斯開腳本 發布:2024-04-24 01:48:22 瀏覽:161