從源碼角度,帶你研究什么是三級緩存


  • Java開發,總會遇到問三級緩存的。
  • 看了很多文章,感覺不是很透徹。打算自己寫一個自以為很詳細的對三級緩存的理解。 有圖文。也有文字概括。受不了動圖的可以看文字概括哦

進入正題:

  • 在開發過程中會遇到循環依賴的問題。 就跟下圖一樣

image

  • Spring在為此設計了三級緩存來解決以上依賴的問題

首先我們得知道 三級緩存里面分別存的什么

一級緩存里存的是成品對象,實例化和初始化都完成了,我們的應用中使用的對象就是一級緩存中的

二級緩存中存的是半成品,用來解決對象創建過程中的循環依賴問題

三級緩存中存的是 ObjectFactory<?> 類型的 lambda 表達式,用於處理存在 AOP 時的循環依賴問題

image

Spring 三級緩存的順序

三級緩存的順序是由查詢循序而來,與在類中的定義順序無關

image

所以第一級緩存:singletonObjects ,第二級緩存:earlySingletonObjects ,第三級緩存:singletonFactories

Spring 的的注入方式有三種:構造方法注入、setter 方法注入、接口注入

接口注入的方式太靈活,易用性比較差,所以並未廣泛應用起來,大家知道有這么一說就好,不要去細扣了

構造方法注入的方式,將實例化與初始化並在一起完成,能夠快速創建一個可直接使用的對象,但它沒法處理循環依賴的問題,了解就好

setter 方法注入的方式,是在對象實例化完成之后,再通過反射調用對象的 setter 方法完成屬性的賦值,能夠處理循環依賴的問題,是后文的基石,必須要熟悉

Spring 源碼分析

  • 下面會從幾種不同的情況來進行源碼跟蹤

1、 沒有依賴,有 AOP

代碼非常簡單: spring-no-dependence

image

  • 上圖的邏輯就是 ClassPathXmlApplicationContext#refresh

  • -> this.finishBeanFactoryInitialization(beanFactory)

  • -> beanFactory.preInstantiateSingletons()

  • -> isFactoryBean 判斷bean, 然后調用 getBean 方法。

  • 接下來調用 doGetBean 方法

image

  • 上圖邏輯是 DefaultSingletonBeanRegistry#getSingleton 判斷是否存在緩存當中,如果沒有則進行創建Bean
  • 可以觀察到
 Map<String, Object> singletonObjects               無 simpleBean 
 Map<String, Object> earlySingletonObjects          無 simpleBean 
 Map<String, ObjectFactory<?>> singletonFactories   無 simpleBean 
 Set<String> singletonsCurrentlyInCreation          有 simpleBean 
  • 說明bean在創建過成中
  • 我們接着從 createBean 往下跟

image

  • 關鍵代碼在 doCreateBean 中,其中有幾個關鍵方法的調用值得大家去跟下
 Map<String, Object> singletonObjects               存儲的 simpleBean 的代理對象
 Map<String, Object> earlySingletonObjects          由始至終都沒有 simpleBean 對象
 Map<String, ObjectFactory<?>> singletonFactories   存入了一會數據立馬就刪除了 並未使用過  
 
 // 以下說的方法均在 AbstractAutowireCapableBeanFactory 類下
 // createBeanInstance 通過反射完成對象的實例化,獲得半成品對象。 給分配內存空間, 即使半成品
 // populateBean 填充半成品屬性, 如果有依賴對象則在這里引入
 // initializeBean 初始化半成品對象
 // applyBeanPostProcessorsAfterInitialization BeanPostProcessor的后置處理,AOP 的代理對象替換就是在這里完成的  
  • 此時:代理對象的創建是在對象實例化完成,並且初始化也完成之后進行的,是對一個成品對象創建代理對象
  • 所以《沒有依賴,有 AOP》 情況下:只用一級緩存就夠了,其他兩個緩存可以不要也能完成對象

2、循環依賴,沒有AOP

代碼依舊非常簡單: spring-circle-simple 此時循環依賴的兩個類是: CircleLoop

對象的創建過程與前面的基本一致,只是多了循環依賴,少了 AOP,所以我們重點關注: populateBeaninitializeBean 方法

先創建的是 Circle 對象,那么我們就從創建它的 populateBean 開始,再開始之前,我們先看看三級緩存中的數據情況

 Map<String, Object> singletonObjects               無 circle 也無 loop 對象
 Map<String, Object> earlySingletonObjects          無 circle 也無 loop 對象
 Map<String, ObjectFactory<?>> singletonFactories   只有 cicle 的 lambda
 Set<String> singletonsCurrentlyInCreation          只有 circle 

image

  • 我們開始跟populateBean,它完成屬性的填充,與循環依賴有關,一定要仔細看,仔細跟

image

circle 對象的屬性 loop 進行填充的時候,去 Spring 容器中找 loop 對象,發現沒有則進行創建,又來到了熟悉的 createBean

此時三級緩存中的數據沒有變化,但是 Set<String> singletonsCurrentlyInCreation 中多了個 loop 標識loop正在創建中

image

loop 實例化完成之后,對其屬性 circle 進行填充,去 Spring 中獲取 circle 對象,又來到了熟悉的 doGetBean

此時一、二級緩存 (singletonObjects``earlySingletonObjects) 中都沒有 circleloop ,而三級緩存中有這兩個

image

通過 getSingleton 獲取circle時,三級緩存調用了 getEarlyBeanReference ,但由於沒有 AOP,所以 getEarlyBeanReference 直接返回了普通的 半成品 circle

然后將 半成品 circle 放到了二級緩存,並將其返回,然后填充到了 loop 對象中

此時的 loop 對象就是一個成品對象了;接着將 loop 對象返回,填充到 circle 對象中,如下如所示

image

我們發現直接將 成品 loop 放到了一級緩存中,二級緩存自始至終都沒有過 loop ,三級緩存雖說存了 loop ,但沒用到就直接 remove 了

此時緩存中的數據,相信大家都能想到了

 Map<String, Object> singletonObjects               無 circle 有 loop 對象
 Map<String, Object> earlySingletonObjects          有 circle 無 loop 對象
 Map<String, ObjectFactory<?>> singletonFactories   無 circle 也無 loop
 Set<String> singletonsCurrentlyInCreation          有 circle 無 loop 因為loop創建完畢了
  • 當 loop 對象完成 創建bean的時候 會調用 DefaultSingletonBeanRegistry#getSingleton -> DefaultSingletonBeanRegistry#addSingleton
  • 將數據對象移動到一級緩存中。二級緩存的 circle 沒用上就刪除了, 只有 circle 存在三級緩存的數據被調用到了。將半成品的 circle 給返回給 loop對象
  • 所以《循環依賴,沒有AOP》情況下:可以減少某個緩存,只需要兩級緩存就夠了

概括:(循環依賴,沒有AOP)

  • 上頭的步驟可概括為:
  • 第一步。doCreateBean 進行 circle 的創建,創建步驟為:
  • circle 的流程:
- `AbstractBeanFactory#doGetBean`                               獲取bean
- -> `AbstractAutowireCapableBeanFactory#createBean`            創建bean
- -> `AbstractAutowireCapableBeanFactory#doCreateBean`          開始創建bean
- -> `AbstractAutowireCapableBeanFactory#addSingletonFactory`   把bean的一個 lambda 到三級緩存去了 singletonFactories
- -> `AbstractAutowireCapableBeanFactory#populateBean`          填充bean
- -> `AbstractAutowireCapableBeanFactory#applyPropertyValues`   檢查到有要添加的一來 進行填充
- -> `BeanDefinitionValueResolver#resolveValueIfNecessary`      注意 ! 這個位置獲取 loop 對象
```java
- 斷點 我們觀察下 三個緩存 Map的存儲情況
```java 
 Map<String, Object> singletonObjects               無 circle 也無 loop 對象
 Map<String, Object> earlySingletonObjects          無 circle 也無 loop 對象
 Map<String, ObjectFactory<?>> singletonFactories   有 circle 也無 loop 對象
 Set<String> singletonsCurrentlyInCreation          有 circle 
  • 第二步 然后 get loop Bean 會重復上面的步驟
- `AbstractBeanFactory#doGetBean`                               獲取bean
- -> `AbstractAutowireCapableBeanFactory#createBean`            創建bean
- -> `AbstractAutowireCapableBeanFactory#doCreateBean`          開始創建bean
- -> `AbstractAutowireCapableBeanFactory#addSingletonFactory`   把bean的一個 lambda 到三級緩存去了 singletonFactories
- -> `AbstractAutowireCapableBeanFactory#populateBean`          填充bean
- -> `AbstractAutowireCapableBeanFactory#applyPropertyValues`   檢查到有要添加的一來 進行填充
- -> `BeanDefinitionValueResolver#resolveValueIfNecessary`      注意 ! 這個位置改了。獲取的是 circle 對象
  • 斷點 我們觀察下 三個緩存 Map的存儲情況
 Map<String, Object> singletonObjects               無 circle 也無 loop 對象
 Map<String, Object> earlySingletonObjects          無 circle 也無 loop 對象
 Map<String, ObjectFactory<?>> singletonFactories   有 circle 有 loop 對象
 Set<String> singletonsCurrentlyInCreation          有 circle 有 loop 說明兩個對象都在創建中 
  • 關鍵點來了:
  • 第三步 相當於程序是第二次進入 circleAbstractBeanFactory#doGetBean
- `AbstractBeanFactory#doGetBean`                                           第二次獲取 circle
- `AbstractBeanFactory#getSingleton(beanName)`                              獲取 Bean 的緩存
- `DefaultSingletonBeanRegistry#getSingleton(beanName, true)`               獲取 Bean 的緩存
- `DefaultSingletonBeanRegistry#isSingletonCurrentlyInCreation(beanName)`   關鍵!! 判斷 circle 這個名字的bean是不是在創建過程
- `this.singletonFactories.get(beanName)`                                   獲取這個 circle 的 lambda 創建函數
- `singletonFactory.getObject()`                                            調用函數 獲取了一個半成品的對象。 也就是 loop 還為空的 circle對象
- `this.earlySingletonObjects.put(beanName, singletonObject)`               將對象加入到二級緩存里面去   earlySingletonObjects 增加了對象
// 附,只有 earlySingletonObjects 新增了一個 circle 對象,其他map 無改變。 並且loop的 singletonFactories 也未使用到
  • 然后就返回了 circle 給到 loop 進行屬性填充
  • 完成 loop 創建 將 loop 在 (earlySingletonObjects、singletonFactories、singletonsCurrentlyInCreation)清除。loop添加對象到 singletonObjects
  • 返回創建好的 loop 給到 circle 的填充屬性流程
  • 填充完畢之后。在(earlySingletonObjects、singletonFactories、singletonsCurrentlyInCreation)清除。 添加circle對象到 singletonObjects
  • 注意 : circle 就算只是半成品 那他也是在bean中是唯一的。 只要 circle 的屬性在后面填充了loop 那么在 loop 的那個單例緩存里面。就會有循環依賴的 circle 對象
  • 其實在整個流程中 circle 會進入到二級緩存當中。但是沒使用。就被remove了
  • loop 在二級緩存從來就沒有出現過。因為不會進入兩次 loop 的 doGetBean流程 。 loop的三級緩存數據也沒使用過就被刪除了。

2、循環依賴,有AOP

代碼還是非常簡單:spring-circle-aop ,在循環依賴的基礎上加了 AOP

比上一種情況多了 AOP,我們來看看對象的創建過程有什么不一樣;同樣是先創建 Circle ,在創建Loop

創建過程與上一種情況大體一樣,只是有小部分區別,跟源碼的時候我會在這些區別上有所停頓,其他的會跳過,大家要仔細看

實例化 Circle ,然后填充 半成品 circle 的屬性 loop ,去 Spring 容器中獲取 loop 對象,發現沒有

則實例化 Loop ,接着填充 半成品 loop 的屬性 circle ,去 Spring 容器中獲取 circle 對象 

這個過程與前一種情況是一致的,就直接跳過了,此時三級緩存中的數據如下:

 Map<String, Object> singletonObjects               無 circle 也無 loop 對象
 Map<String, Object> earlySingletonObjects          無 circle 也無 loop 對象
 Map<String, ObjectFactory<?>> singletonFactories   有 circle 有 loop 對象
 Set<String> singletonsCurrentlyInCreation          有 circle 有 loop 說明兩個對象都在創建中 

image

我們發現從第三級緩存獲取 circle 的時候,調用了 getEarlyBeanReference 創建了 半成品circle的代理對象

將 半成品 circle 的代理對象放到了第二級緩存中,並將代理對象返回賦值給了 半成品 loopcircle 屬性

注意:此時是在進行 loop 的初始化,但卻把 半成品 circle 的代理對象提前創建出來了

loop 的初始化還未完成,我們接着往下看,又是一個重點,仔細看

image

initializeBean 方法中完成了 半成品 loop 的初始化,並在最后創建了 loop 成品 的代理對象

loop 代理對象創建完成之后會將其放入到第一級緩存中(移除第三級緩存中的 loop ,第二級緩存自始至終都沒有 loop

然后將 loop 代理對象返回並賦值給 半成品 circle 的屬性 loop ,接着進行 半成品 circleinitializeBean

image

因為 circle 的代理對象已經生成過了(在第二級緩存中),所以不用再生成代理對象了;將第二級緩存中的 circle 代理對象移到第一級緩存中,並返回該代理對象

此時各級緩存中的數據情況如下(普通circle loop 對象在各自代理對象的 target 中)

 Map<String, Object> singletonObjects               有 circle 代理對象 有 loop 代理對象
 Map<String, Object> earlySingletonObjects          無 circle 無 loop 對象
 Map<String, ObjectFactory<?>> singletonFactories   無 circle 無 loop 對象
 Set<String> singletonsCurrentlyInCreation          無 circle 無 loop 

我們回顧下這種情況下各級緩存的存在感,一級緩存仍是存在感十足,二級緩存有存在感,三級緩存挺有存在感

      第三級緩存提前創建 circle 代理對象,不提前創建則只能給 loop 對象的屬性 circle 賦值成 半成品 circle ,那么 loop 對象中的 circle 對象就無 AOP 增強功能了

      第二級緩存用於存放 circle 代理,用於解決循環依賴;也許在這個示例體現的不夠明顯,因為依賴比較簡單,依賴稍復雜一些,就能感受到了
      
第一級緩存存放的是對外暴露的對象,可能是代理對象,也可能是普通對象

所以此種情況下:三級緩存一個都不能少

概括: (2、循環依賴,有AOP)

  • 概括:(循環依賴,沒有AOP)基本一致
  • 在第三步發生變化:
- `AbstractBeanFactory#doGetBean`                                           第二次獲取 circle
- `AbstractBeanFactory#getSingleton(beanName)`                              獲取 Bean 的緩存
- `DefaultSingletonBeanRegistry#getSingleton(beanName, true)`               獲取 Bean 的緩存
- `DefaultSingletonBeanRegistry#isSingletonCurrentlyInCreation(beanName)`   判斷 circle 這個名字的bean是不是在創建過程
- `this.singletonFactories.get(beanName)`                                   獲取這個 circle 的 lambda 創建函數 
- `singletonFactory.getObject()`                                            調用函數 獲取了一個半成品的對象。(注意!! 有AOP環繞的對象在該位置會創建代理對象, 並且將代理對象 通過 AbstractAutoProxyCreator#getEarlyBeanReference 同步到AOP的創建類里邊。為了后面的使用) 也就是 loop 還為空的 circle對象
- `this.earlySingletonObjects.put(beanName, singletonObject)`               將對象加入到二級緩存里面去   earlySingletonObjects 增加了對象
// 附,只有 earlySingletonObjects 新增了一個 circle 對象,其他map 無改變。 
  • 然后就完成了 loop 的創建。
  • 然后進行完 circle 填充之后。
- -> `AbstractAutowireCapableBeanFactory#populateBean`          填充完bean之后
- -> `AbstractAutowireCapableBeanFactory#initializeBean`        進行 circle 的初始化
- -> `AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization`        bean的后置通知。此位置會進行 bean AOP的環繞 返回代理對象
- 由於在上方 loop 獲取 circle 的時候不是已經創建了個代理對象了嗎。那么這個aop就不能在新建一個代理類了。不然不一致
- 接着往下看
- -> `AbstractAutoProxyCreator#postProcessAfterInitialization`        創建代理對象
- -> `if (this.earlyProxyReferences.remove(cacheKey) != bean)`        這個時候  二級緩存派上用場了。在這里。判斷是否已經有代理類了。如果有代理類則不新建代理類對象。
// 這樣 circle 的代理就不會被重復創建了。  二級緩存也派上了用場

4、循環依賴 + AOP + 刪除第三級緩存

沒有依賴,有AOP 這種情況中,我們知道 AOP 代理對象的生成是在成品對象創建完成之后創建的,這也是 Spring 的設計原則,代理對象盡量推遲創建

循環依賴 + AOP 這種情況中, circle 代理對象的生成提前了,因為必須要保證其 AOP 功能,但 loop 代理對象的生成還是遵循的 Spring 的原則

如果我們打破這個原則,將代理對象的創建邏輯提前,那是不是就可以不用三級緩存了,而只用兩級緩存了呢?

代碼依舊簡單:spring-circle-custom ,只是對 Spring 的源碼做了非常小的改動,改動如下

image

去除了第三級緩存,並將代理對象的創建邏輯提前,置於實例化之后,初始化之前;

總結

  1、三級緩存各自的作用

    第一級緩存存的是對外暴露的對象,也就是我們應用需要用到的

    第二級緩存的作用是為了處理循環依賴的對象創建問題,里面存的是半成品對象或半成品對象的代理對象

    第三級緩存的作用處理存在 AOP + 循環依賴的對象創建問題,能將代理對象提前創建

  2、Spring 為什么要引入第三級緩存

    嚴格來講,第三級緩存並非缺它不可,因為可以提前創建代理對象

    提前創建代理對象只是會節省那么一丟丟內存空間,並不會帶來性能上的提升,但是會破環 Spring 的設計原則

    Spring 的設計原則是盡可能保證普通對象創建完成之后,再生成其 AOP 代理(盡可能延遲代理對象的生成)

    所以 Spring 用了第三級緩存,既維持了設計原則,又處理了循環依賴;犧牲那么一丟丟內存空間是願意接受的


免責聲明!

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



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