當前位置:首頁 » 編程語言 » 源碼分析java

源碼分析java

發布時間: 2023-05-04 19:32:22

1. Reference 和 Reference queue 精髓源碼分析

每個Reference狀態列表是Active、Pending、Enqueued、Inactive (本文也將基於以下這四種狀態做出源碼解讀)

狀態間主要的標識:
用Reference的成員變數queue與next(類似於單鏈表中的next)來標識

有了這些約束, GC 只需要檢測next欄位就可以知道是否需要對該引用對象(reference)採取特殊處理

什麼特殊處理?
GC在狀態active發送變化時,會主動把 weakReference中的 reference置null,PhantomReference 中的 reference 無視 (PhantomReference的get方法重寫返回null,可能就因為這原因就沒有置null了),並給next賦值this,以及pending等。(別問我為啥,測出來的。。。。培訓出來就這水平了)

描述:
active 狀態下的reference會受到GC的特別對待,GC在發現對象的可達性變成合適的狀態後將變更reference狀態,具體是變更為pending還是Inactive,取決於是否注冊了 queue

特點:

源碼分析:

描述:
pending-Reference列表中的引用都是這個狀態。
它們等著被內部線程ReferenceHandler處理(調用ReferenceQueue.enqueue方法)
沒有注冊的實例不會進入這個狀態!

特點:

源碼分析:

3.似乎明白了 ReferenceQueue.ENQUEUED 的含義

描述:
調用ReferenceQueue.enqueued方法後的引用處於這個狀態中。
沒有注冊的實例不會進入這個狀態。

特點:

源碼分析:
可以看出 整體是一種先進後出的棧結構。head作為棧頭。r.next指向成員變數 head,如果head為null,那麼就指向自己(為了後面的壓棧好找元素)

描述:
最終狀態,處於這個狀態的引用對象,狀態不會在改變。

兩種途徑到這個狀態:
1.referenceQueue 調用 reallyPoll ,
2.不注冊 referenceQueue,reference state變化時。

特點:

源碼分析:

引用:
https://liujiacai.net/blog/2015/09/27/java-weakhashmap/#%E5%BC%B1%E5%BC%95%E7%94%A8%EF%BC%88weak-reference%EF%BC%89

2. 源碼分析之AtomicInteger

AtomicInteger 是 java.util.concurrent.atomic 包下的類,作用是提供原子操作 Integer 類。

我們知道在 Java 中, i++、++i 這種操作並不是線程安全的。主要是因為類似於 ++ 操作會被編譯為2條操作指令,而多線程環境下CPU在執行過程可能會中斷切換到別的線程,無法保證2條操作指令的原子性,所以是線程不安全。針對這種情況,我們可能會想到利用 synchronize 關鍵字實現線程同步,保證 ++ 操作的原子性,的確這是一種有效的方法,但我們還有一種選擇-- AtomicInteger 。

首先先看一下 AtomicInteger 類的類變數。可以看出有個 unsafe 的類變數。

Unsafe 類是用來在任意內存亮卜地址位置處讀寫數據,可見,對於普通用戶來說,使用起來還是比較危險的。 AtomicInteger 類本質就是利用 Unsafe.compareAndSwapInt 這個 CAS 操作方法來保證 Integer 操作的原子性。仿鍵隱

看一下 AtomicInteger 的對 int 的加法操作。

可以看出調用的是 Unsafe 類中的 getAndAddInt 方法,可以繼續看看 getAndAddInt 的實現:

CAS 有3個操作數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改為B,否則什麼都不做。 AtomicInteger 類實則是利用這一特性來保證操作的原子性,可以說通過 CAS 實現的。

CAS 雖然可以解決原子性問題,但也有其他問題,因為 CAS 需要在操作值的時候檢查下值有沒有發生變化備廳,如果沒有發生變化則更新,但是如果一個值原來是A,變成了B,又變成了A,那麼使用 CAS 進行檢查時會發現它的值沒有發生變化,但是實際上卻變化了。

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. JAVA源碼解析的時候 Character.MIN_RADIX為什麼=2

『』佔一個位,你想一下,如果值是1,那麼你怎麼輸入漢字

Character類的使用方法
Character:字元類型
1、屬性。
static int MIN_RADIX :返回最小基數。

5. HashMap源碼分析

HashMap(jdk 1.8)
  使用哈希表存儲鍵值對,底層是一個存儲Node的table數組。其基本的運行邏輯是,table數組的每一個元素是一個槽,用於存儲Node對象,向Hash表中插入鍵值對時,首先計算鍵的hash值,並對數組容量(即數組長度)取余,定位該鍵值對應放在哪個槽中,若槽中已經存在元素(即哈希沖突),將Node節點插入到沖突鏈表尾端,當該槽中結點超過一定數量(默認為8)並且哈希表容量超過一定值(默認為64)時,對該鏈表進行樹化。低於一定數量(默認為6)後進行resize時,樹形結構又會鏈表化。當哈希表的總節點數超過閾值時,對哈希表進行擴容,容量翻倍。

由於哈希表需要用到key的哈希函數,因此對於自定義類作為key使用哈希表時,需要重寫哈希函數,保證相等的key哈希值相同。

由於擴容或樹化過程Node節點的位置會發生改變,因此哈希表是無序的,不同時期遍歷同一張哈希表,得到的節點順序會不同。

成員變數

    Hash表的成員變數主要有存儲鍵值對的table數組,size,裝載因子loadFactory,閾值theshold,容量capacity。

table數組:

哈希表的實質就是table數組,它用來存儲鍵值對。哈希表中內部定義了Node類來封裝鍵值對。

 Node類實現了Map的Entry介面。包括key,value,下一個Node指針和key的hash值。

 容量Capacity:

HashMap中沒有專門的屬性存儲容量,容量等於table.lenth,即數組的長度,默認初始化容量為16。初始化時,可以指定哈希表容量,但容量必須是2的整數次冪,在初始化過程中,會調用tableSizeFor函數,得到一個不小於指定容量的2的整數次冪,暫時存入到threshold中。

注意,若容量設置過大,那麼遍歷整個哈希表需要耗費更多的時間。

    閾值threshold:

指定當前hash表最多存儲多少個鍵值對,當size超過閾值時,hash表進行擴容,擴容後容量翻倍。

   loadFactory:

threshold=capacity*loadFactory。

裝填因子的大小決定了哈希表存儲鍵值對數量的能力,它直接影響哈希表的查找性能。初始化時可以指定loadFactory的值,默認為0.75。

 初始化過程

哈希鬧做表有3個構造函數,用戶可以指定哈希表的初始容量和裝填因子,但初始化過程只是簡單填入這兩個參數,table數組並沒有初始化。注意,這里的initialCapacity會向上取2的整數次冪並暫時保存到threshold中。

table數組的初始化是在第一次插入鍵值對時觸發,實際在resize函數中進行初始化。

hash過程與下標計算

哈希表是通過key的哈希值決定Node放入哪個槽中,察毀 在java8中 ,hash表沒有直接用key的哈希值,而是自定義了hash函數。

哈希表中的key可以為null,若為null,哈希值為0,否則哈希值(一共32位)的高16位不變,低16位與高16位進行異或操作,得到新的哈希值。(h >>> 16,液沒衡表示無符號右移16位,高位補0,任何數跟0異或都是其本身,因此key的hash值高16位不變。)

之所以這樣處理,是與table的下標計算有關

因為,table的長度都是2的冪,因此index僅與hash值的低n位有關(n-1為0000011111的形式),hash值的高位都被與操作置為0了,相當於hash值對n取余。

由於index僅與hash值的低n位有關,把哈希值的低16位與高16位進行異或處理,可以讓Node節點能更隨機均勻得放入哈希表中。

get函數:

V  get(Object key) 通過給定的key查找value

先計算key的哈希值,找到對應的槽,遍歷該槽中所有結點,如果結點中的key與查找的key相同或相等,則返回value值,否則返回null。

注意,如果返回值是null,並不一定是沒有這種KV映射,也有可能是該key映射的值value是null,即key-null的映射。也就是說,使用get方法並不能判斷這個key是否存在,只能通過containsKey方法來實現。

put函數

V put(K key, V value) 存放鍵值對

計算key的哈希值,找到哈希表中對應的槽,遍歷該鏈表,如果發現已經存在包含該key的Node,則把新的value放入該結點中,返回該結點的舊value值(舊value可能是null),如果沒有找到,則創建新結點插入到 鏈表尾端 (java7使用的是頭插法),返回null。插入結點後需要判斷該鏈表是否需要樹化,並且判斷整個哈希表是否需要擴容。

由該演算法可以看出,哈希表中不會有兩個key相同的鍵值對。

resize函數

    resize函數用來初始化或擴容table數組。主要做了兩件事。

1.根據table數組是否為空判斷初始化還是擴容,計算出相應的newCap和newThr,新建table數組並設置新的threshold。

2.若是進行擴容,則需要將原數組中的Node移植到新的數組中。

由於數組擴容,原來鍵值對可能存儲在新數組不同的槽中。但由於鍵值對位置是對數組容量取余(index=hash(key)&(Capacity-1)),由於Capacity固定為2的整數次冪,並新的Capacity(newCap)是舊的兩倍(oldCap)。因此,只需要判斷每個Node中的hash值在oldCap二進制那一位上是否為0(hash&oldCap==0),若為0,對newCap取余值與oldCap相同,Node在新數組中槽的位置不變,若為1,新的位置是就得位置加一個oldCap值。

因此hashMap的移植演算法是,遍歷舊的槽:

若槽為空,跳過。

若槽只有一個Node,計算該Node在新數組中的位置,放入新槽中。

若槽是一個鏈表,則遍歷這個鏈表,分別用loHead,loTail,hiHead,hiTail兩組指針建立兩個鏈表,把hash值oldCap位為0的Node節點放入lo鏈表中,hash值oldCap位為1的節點放入hi鏈表中,之後將兩個鏈表分別插入到新數組中,實現原鍵值對的移植。

lo鏈表放入新數組原來位置,hi鏈表放入新數組原來位置+oldCap中

樹化過程 :

當一個槽中結點過多時,哈希表會將鏈表轉換為紅黑樹,此處挖個坑。

[總結] java8中hashMap的實現原理與7之間的區別:

1.hash值的計算方法不同 2.鏈表採用尾插法,之前是頭插法 3.加入了紅黑樹結構

6. [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 。

其主要方法執行如下:

7. 如何讀JAVA源碼

最好下個編輯器,editplus,gvim之類的,我用的是gvim,當然有myeclipse之類的軟體就更好,將代碼引進去,然後從主類開始,先看一遍主類,大體知道是幹嘛的就好了,然後再細看,從上到下,當看到新類時,再轉過去看那個類,看懂了再回主類繼續,

熱點內容
萬科海上傳奇二期 發布:2024-11-01 14:22:52 瀏覽:59
u盤文件夾是空的 發布:2024-11-01 14:19:57 瀏覽:402
python包含字元串 發布:2024-11-01 14:19:17 瀏覽:479
c語言的精華 發布:2024-11-01 14:19:02 瀏覽:588
steam截圖文件夾 發布:2024-11-01 14:18:59 瀏覽:613
ipad怎麼往安卓傳照片 發布:2024-11-01 14:18:19 瀏覽:508
我的電腦沒有文件夾選項 發布:2024-11-01 14:13:55 瀏覽:546
vb創建資料庫表 發布:2024-11-01 14:11:55 瀏覽:872
sql聯合表 發布:2024-11-01 14:03:25 瀏覽:962
linux編程gcc 發布:2024-11-01 14:02:41 瀏覽:705