本次博客的目標(biāo)
1. 手寫spring循環(huán)依賴的整個過程
2. spring怎么解決循環(huán)依賴
3. 為什么要二級緩存和三級緩存
4. spring有沒有解決構(gòu)造函數(shù)的循環(huán)依賴
5. spring有沒有解決多例下的循環(huán)依賴.
一.? 什么是循環(huán)依賴?
如下圖所示:
A類依賴了B類, 同時B類有依賴了A類. 這就是循環(huán)依賴, 形成了一個閉環(huán)
如上圖: A依賴了B, B同時依賴了A和C , C依賴了A. 這也是循環(huán)依賴. , 形成了一個閉環(huán)
那么, 如果出現(xiàn)循環(huán)依賴, spring是如何解決循環(huán)依賴問題的呢?
二. 模擬循環(huán)依賴
2.1 復(fù)現(xiàn)循環(huán)依賴
我們定義三個類:
1. 新增類InstanceA
package com.lxl.www.circulardependencies;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype")public class InstanceA {? ? @Autowired? ? private InstanceB instanceB;? ? public InstanceA() {
? ? ? ? System.out.println("調(diào)用 instanceA的構(gòu)造函數(shù)");
? ? }? ? public InstanceA(InstanceB instanceB) {? ? ? ? this.instanceB = instanceB;
? ? }? ? public void say(){
? ? ? ? System.out.println( "I am A");
? ? }? ? public InstanceB getInstanceB() {? ? ? ? return instanceB;
? ? }? ? public void setInstanceB(InstanceB instanceB) {? ? ? ? this.instanceB = instanceB;
? ? }
}
這是InstanceA, 里面引用了InstanceB.
2. 新增類instanceB
網(wǎng)絡(luò)異常 取消重新上傳
package com.lxl.www.circulardependencies;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype")public class InstanceB {? ? @Autowired? ? private InstanceA instanceA;? ? public InstanceB() {
? ? ? ? System.out.println("調(diào)用 instanceB的構(gòu)造函數(shù)");
? ? }? ? public InstanceA getInstanceA() {? ? ? ? return instanceA;
? ? }? ? public void setInstanceA(InstanceA instanceA) {? ? ? ? this.instanceA = instanceA;
? ? }
}
網(wǎng)絡(luò)異常 取消重新上傳
這是InstanceB, 在里面有引用了InstanceA
3:模擬spring是如何創(chuàng)建Bean的
這個在前面已經(jīng)說過了, 首先會加載配置類的后置處理器, 將其解析后放入到beanDefinitionMap中. 然后加載配置類, 也將其解析后放入beanDefinitionMap中. 最后解析配置類. 我們這里直接簡化掉前兩步, 將兩個類放入beanDefinitionMap中. 主要模擬第三步解析配置類. 在解析的過程中, 獲取bean的時候會出現(xiàn)循環(huán)依賴的問題循環(huán)依賴.
第一步: 將兩個類放入到beanDefinitionMap中
網(wǎng)絡(luò)異常 取消重新上傳
? ? =? RootBeanDefinition(InstanceA.=? RootBeanDefinition(InstanceB.
網(wǎng)絡(luò)異常 取消重新上傳
上面的代碼結(jié)構(gòu)很簡單, 再看一下注釋應(yīng)該就能明白了. 這里就是模擬spring將配置類解析放入到beanDefinitionMap的過程.
第二步: 循環(huán)創(chuàng)建bean
首先,我們已經(jīng)知道, 創(chuàng)建bean一共有三個步驟: 實例化, 屬性賦值, 初始化.
網(wǎng)絡(luò)異常 取消重新上傳
而在屬性賦值的時候, 會判斷是否引用了其他的Bean, 如果引用了, 那么需要構(gòu)建此Bean. 下面來看一下代碼
網(wǎng)絡(luò)異常 取消重新上傳
/**
? ? * 獲取bean, 根據(jù)beanName獲取? ? */
? ? public static Object getBean(String beanName) throws Exception {/**
? ? ? ? * 第一步: 實例化
? ? ? ? * 我們這里是模擬, 采用反射的方式進(jìn)行實例化. 調(diào)用的也是最簡單的無參構(gòu)造函數(shù)? ? ? ? */
? ? ? ? RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
? ? ? ? Class<?> beanClass = beanDefinition.getBeanClass();? ? ? ? // 調(diào)用無參的構(gòu)造函數(shù)進(jìn)行實例化
? ? ? ? Object instanceBean = beanClass.newInstance();
? ? /**
? ? ? ? *? 第二步: 屬性賦值
? ? ? ? *? instanceA這類類里面有一個屬性, InstanceB. 所以, 先拿到 instanceB, 然后在判斷屬性頭上有沒有Autowired注解.
? ? ? ? *? 注意: 這里我們只是判斷有沒有Autowired注解. spring中還會判斷有沒有@Resource注解. @Resource注解還有兩種方式, 一種是name, 一種是type? ? ? ? */
? ? ? ? Field[] declaredFields = beanClass.getDeclaredFields();? ? ? ? for (Field declaredField: declaredFields) {? ? ? ? ? ? // 判斷每一個屬性是否有@Autowired注解
? ? ? ? ? ? Autowired annotation = declaredField.getAnnotation(Autowired.class);? ? ? ? ? ? if (annotation != null) {? ? ? ? ? ? ? ? // 設(shè)置這個屬性是可訪問的
? ? ? ? ? ? ? ? declaredField.setAccessible(true);? ? ? ? ? ? ? ? // 那么這個時候還要構(gòu)建這個屬性的bean.
? ? ? ? ? ? ? ? /*
? ? ? ? ? ? ? ? * 獲取屬性的名字
? ? ? ? ? ? ? ? * 真實情況, spring這里會判斷, 是根據(jù)名字, 還是類型, 還是構(gòu)造函數(shù)來獲取類.
? ? ? ? ? ? ? ? * 我們這里模擬, 所以簡單一些, 直接根據(jù)名字獲取.? ? ? ? ? ? ? ? */
? ? ? ? ? ? ? ? String name = declaredField.getName();? ? ? ? ? ? ? ? /**
? ? ? ? ? ? ? ? * 這樣, 在這里我們就拿到了 instanceB 的 bean? ? ? ? ? ? ? ? */
? ? ? ? ? ? ? ? Object fileObject = getBean(name);? ? ? ? ? ? ? ? // 為屬性設(shè)置類型
? ? ? ? ? ? ? ? declaredField.set(instanceBean, fileObject);
? ? ? ? ? ? }
? ? ? ? }? ? ? ? /**
? ? ? ? * 第三步: 初始化
? ? ? ? * 初始化就是設(shè)置類的init-method.這個可以設(shè)置也可以不設(shè)置. 我們這里就不設(shè)置了? ? ? ? */
? ? ? ? return instanceBean;
? ? }
網(wǎng)絡(luò)異常 取消重新上傳
我們看到如上代碼.
第一步: 實例化:使用反射的方式, 根據(jù)beanName查找構(gòu)建一個實例bean.
第二步: 屬性賦值:判斷屬性中是否有@Autowired屬性, 如果有這個屬性, 那么需要構(gòu)建bean. 我們發(fā)現(xiàn)在為InstanceA賦值的時候, 里面引用了InstanceB, 所以去創(chuàng)建InstanceB, 而創(chuàng)建InstanceB的時候, 發(fā)現(xiàn)里面又有InstanceA, 于是又去創(chuàng)建A. 然后以此類推,繼續(xù)判斷. 就形成了死循環(huán). 無法走出這個環(huán).這就是循環(huán)依賴
第三步: 初始化:調(diào)用init-method, 這個方法不是必須有, 所以,我們這里不模擬了
看看如下圖所示
網(wǎng)絡(luò)異常 取消重新上傳
紅色部分就形成了循環(huán)依賴.
4: 增加一級緩存, 解決循環(huán)依賴的問題.
我們知道上面進(jìn)行了循環(huán)依賴了. 其實, 我們的目標(biāo)很簡單, 如果一個類創(chuàng)建過了, 那么就請不要在創(chuàng)建了.
所以, 我們增加一級緩存
網(wǎng)絡(luò)異常 取消重新上傳
// 一級緩存
? ? private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(); /**
? ? * 獲取bean, 根據(jù)beanName獲取? ? */
? ? public static Object getBean(String beanName) throws Exception {? ? ? ? // 增加一個出口. 判斷實體類是否已經(jīng)被加載過了
? ? ? ? Object singleton = getSingleton(beanName);? ? ? ? if (singleton != null) {? ? ? ? ? ? return singleton;
? ? ? ? }? ? ? ? /**
? ? ? ? * 第一步: 實例化
? ? ? ? * 我們這里是模擬, 采用反射的方式進(jìn)行實例化. 調(diào)用的也是最簡單的無參構(gòu)造函數(shù)? ? ? ? */
? ? ? ? RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
? ? ? ? Class<?> beanClass = beanDefinition.getBeanClass();? ? ? ? // 調(diào)用無參的構(gòu)造函數(shù)進(jìn)行實例化
? ? ? ? Object instanceBean = beanClass.newInstance();? ? ? ? /**
? ? ? ? * 第二步: 放入到一級緩存? ? ? ? */
? ? ? ? singletonObjects.put(beanName, instanceBean);? ? ? ? /**
? ? ? ? *? 第三步: 屬性賦值
? ? ? ? *? instanceA這類類里面有一個屬性, InstanceB. 所以, 先拿到 instanceB, 然后在判斷屬性頭上有沒有Autowired注解.
? ? ? ? *? 注意: 這里我們只是判斷有沒有Autowired注解. spring中還會判斷有沒有@Resource注解. @Resource注解還有兩種方式, 一種是name, 一種是type? ? ? ? ? */
? ? ? ? Field[] declaredFields = beanClass.getDeclaredFields();? ? ? ? for (Field declaredField: declaredFields) {? ? ? ? ? ? // 判斷每一個屬性是否有@Autowired注解
? ? ? ? ? ? Autowired annotation = declaredField.getAnnotation(Autowired.class);? ? ? ? ? ? if (annotation != null) {? ? ? ? ? ? ? ? // 設(shè)置這個屬性是可訪問的
? ? ? ? ? ? ? ? declaredField.setAccessible(true);? ? ? ? ? ? ? ? // 那么這個時候還要構(gòu)建這個屬性的bean.
? ? ? ? ? ? ? ? /*
? ? ? ? ? ? ? ? * 獲取屬性的名字
? ? ? ? ? ? ? ? * 真實情況, spring這里會判斷, 是根據(jù)名字, 還是類型, 還是構(gòu)造函數(shù)來獲取類.
? ? ? ? ? ? ? ? * 我們這里模擬, 所以簡單一些, 直接根據(jù)名字獲取.? ? ? ? ? ? ? ? */
? ? ? ? ? ? ? ? String name = declaredField.getName();? ? ? ? ? ? ? ? /**
? ? ? ? ? ? ? ? * 這樣, 在這里我們就拿到了 instanceB 的 bean? ? ? ? ? ? ? ? */
? ? ? ? ? ? ? ? Object fileObject = getBean(name);? ? ? ? ? ? ? ? // 為屬性設(shè)置類型
? ? ? ? ? ? ? ? declaredField.set(instanceBean, fileObject);
? ? ? ? ? ? }
? ? ? ? }? ? ? ? /**
? ? ? ? * 第四步: 初始化
? ? ? ? * 初始化就是設(shè)置類的init-method.這個可以設(shè)置也可以不設(shè)置. 我們這里就不設(shè)置了? ? ? ? */
? ? ? ? return instanceBean;
? ? }
網(wǎng)絡(luò)異常 取消重新上傳
還是上面的獲取bean的流程, 不一樣的是, 這里增加了以及緩存. 當(dāng)我們獲取到bean實例以后, 將其放入到緩存中. 下次再需要創(chuàng)建之前, 先去緩存里判斷,是否已經(jīng)有了, 如果沒有, 那么再創(chuàng)建.
這樣就給創(chuàng)建bean增加了一個出口. 不會循環(huán)創(chuàng)建了.
網(wǎng)絡(luò)異常 取消重新上傳
如上圖所示, 在@Autowired的時候, 增加了一個出口. 判斷即將要創(chuàng)建的類是否已經(jīng)存在, 如果存在了, 那么就直接返回, 不在創(chuàng)建
雖然使用了一級緩存解決了循環(huán)依賴的問題, 但要是在多線程下, 這個依賴可能就會出現(xiàn)問題.
比如: 有兩個線程, 同時創(chuàng)建instanceA 和instanceB, instanceA和instanceB都引用了instanceC. 他們同步進(jìn)行, 都去創(chuàng)建instanceC. 首先A去創(chuàng)建, A在實例化instanceC以后就將其放入到一級緩存了, 這時候, B去一級緩存里拿. 此時拿到的instanceC是不完整的. 后面的屬性賦值, 初始化都還沒有執(zhí)行呢. 所以, 我們增加耳機(jī)緩存來解決這個問題.
5. 增加二級緩存, 區(qū)分完整的bean和純凈的bean.
網(wǎng)絡(luò)異常 取消重新上傳
public class MainStart {? ? private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();? ? // 一級緩存
? ? private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();? ? // 二級緩存
? ? private static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();? ? /**
? ? * 讀取bean定義, 當(dāng)然在spring中肯定是根據(jù)配置 動態(tài)掃描注冊的
? ? *
? ? * InstanceA和InstanceB都有注解@Component, 所以, 在spring掃描讀取配置類的時候, 會把他們兩個掃描到BeanDefinitionMap中.
? ? * 這里, 我們省略這一步, 直接將instanceA和instanceB放到BeanDefinitionMap中.? ? */
? ? public static void loadBeanDefinitions(){
? ? ? ? RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class);
? ? ? ? RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class);
? ? ? ? beanDefinitionMap.put("instanceA", aBeanDefinition);
? ? ? ? beanDefinitionMap.put("instanceB", bBeanDefinition);
? ? }? ? public static void main(String[] args) throws Exception {? ? ? ? // 第一步: 掃描配置類, 讀取bean定義? ? ? ? loadBeanDefinitions();? ? ? ? // 第二步: 循環(huán)創(chuàng)建bean
? ? ? ? for (String key: beanDefinitionMap.keySet()) {? ? ? ? ? ? // 第一次: key是instanceA, 所以先創(chuàng)建A類? ? ? ? ? ? getBean(key);
? ? ? ? }? ? ? ? // 測試: 看是否能執(zhí)行成功
? ? ? ? InstanceA instanceA = (InstanceA) getBean("instanceA");
? ? ? ? instanceA.say();
? ? }? ? /**
? ? * 獲取bean, 根據(jù)beanName獲取? ? */
? ? public static Object getBean(String beanName) throws Exception {? ? ? ? // 增加一個出口. 判斷實體類是否已經(jīng)被加載過了
? ? ? ? Object singleton = getSingleton(beanName);
? ? ? ? if (singleton != null) {
? ? ? ? ? ? return singleton;
? ? ? ? }? ? ? ? /**
? ? ? ? * 第一步: 實例化
? ? ? ? * 我們這里是模擬, 采用反射的方式進(jìn)行實例化. 調(diào)用的也是最簡單的無參構(gòu)造函數(shù)? ? ? ? */
? ? ? ? RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
? ? ? ? Class<?> beanClass = beanDefinition.getBeanClass();? ? ? ? // 調(diào)用無參的構(gòu)造函數(shù)進(jìn)行實例化
? ? ? ? Object instanceBean = beanClass.newInstance();? ? ? ? /**
? ? ? ? * 第二步: 放入到二級緩存
? ? ? ? */
? ? ? ? earlySingletonObjects.put(beanName, instanceBean);? ? ? ? /**
? ? ? ? *? 第三步: 屬性賦值
? ? ? ? *? instanceA這類類里面有一個屬性, InstanceB. 所以, 先拿到 instanceB, 然后在判斷屬性頭上有沒有Autowired注解.
? ? ? ? *? 注意: 這里我們只是判斷有沒有Autowired注解. spring中還會判斷有沒有@Resource注解. @Resource注解還有兩種方式, 一種是name, 一種是type? ? ? ? ? */
? ? ? ? Field[] declaredFields = beanClass.getDeclaredFields();? ? ? ? for (Field declaredField: declaredFields) {? ? ? ? ? ? // 判斷每一個屬性是否有@Autowired注解
? ? ? ? ? ? Autowired annotation = declaredField.getAnnotation(Autowired.class);? ? ? ? ? ? if (annotation != null) {? ? ? ? ? ? ? ? // 設(shè)置這個屬性是可訪問的
? ? ? ? ? ? ? ? declaredField.setAccessible(true);? ? ? ? ? ? ? ? // 那么這個時候還要構(gòu)建這個屬性的bean.
? ? ? ? ? ? ? ? /*
? ? ? ? ? ? ? ? * 獲取屬性的名字
? ? ? ? ? ? ? ? * 真實情況, spring這里會判斷, 是根據(jù)名字, 還是類型, 還是構(gòu)造函數(shù)來獲取類.
? ? ? ? ? ? ? ? * 我們這里模擬, 所以簡單一些, 直接根據(jù)名字獲取.? ? ? ? ? ? ? ? */
? ? ? ? ? ? ? ? String name = declaredField.getName();? ? ? ? ? ? ? ? /**
? ? ? ? ? ? ? ? * 這樣, 在這里我們就拿到了 instanceB 的 bean? ? ? ? ? ? ? ? */
? ? ? ? ? ? ? ? Object fileObject = getBean(name);? ? ? ? ? ? ? ? // 為屬性設(shè)置類型
? ? ? ? ? ? ? ? declaredField.set(instanceBean, fileObject);
? ? ? ? ? ? }
? ? ? ? }? ? ? ? /**
? ? ? ? * 第四步: 初始化
? ? ? ? * 初始化就是設(shè)置類的init-method.這個可以設(shè)置也可以不設(shè)置. 我們這里就不設(shè)置了? ? ? ? */
? ? ? ? /**
? ? ? ? * 第二步: 放入到一級緩存? ? ? ? */
? ? ? ? singletonObjects.put(beanName, instanceBean);? ? ? ? return instanceBean;
? ? }? ? /**
? ? * 判斷是否是循環(huán)引用的出口.
? ? * @param beanName
? ? * @return? ? */
? ? private static Object getSingleton(String beanName) {? ? ? ? // 先去一級緩存里拿,如果一級緩存沒有拿到,去二級緩存里拿
? ? ? ? if (singletonObjects.containsKey(beanName)) {? ? ? ? ? ? return singletonObjects.get(beanName);
? ? ? ? } else if (earlySingletonObjects.containsKey(beanName)){? ? ? ? ? ? return earlySingletonObjects.get(beanName);
? ? ? ? } else {? ? ? ? ? ? return null;
? ? ? ? }
? ? }}
網(wǎng)絡(luò)異常 取消重新上傳
如上圖所示,增加了一個二級緩存. 首先, 構(gòu)建出instanceBean以后, 直接將其放入到二級緩存中. 這時只是一個純凈的bean, 里面還沒有給屬性賦值, 初始化. 在給屬性賦值完成, 初始化完成以后, 在將其放入到一級緩存中.
我們判斷緩存中是否有某個實例bean的時候, 先去一級緩存中判斷是否有完整的bean, 如果沒有, 就去二級緩存中判斷有沒有實例化過這個bean.
總結(jié): 一級緩存和二級緩存的作用
一級緩存: 解決循環(huán)依賴的問題二級緩存: 在創(chuàng)建實例bean和放入到一級緩存之間還有一段間隙. 如果在這之間從一級緩存拿實例, 肯定是返回null的. 為了避免這個問題, 增加了二級緩存.
我們都知道spring中有一級緩存, 二級緩存, 三級緩存. 一級緩存和二級緩存的作用我們知道了, 那么三級緩存有什么用呢?
6. 增加三級緩存
三級緩存有什么作用呢? 這個問題眾說紛紜, 有說代理, 有說AOP. 其實AOP的問題可以用二級緩存來解決. 下面就來看看AOP如何用二級緩存解決.
創(chuàng)建AOP動態(tài)代理 (不是耦合的, 采用解耦的, 通過BeanPostProcessor bean的后置處理器來創(chuàng)建). 之前講過, 如下圖
在初始化之后, 調(diào)用Bean的后置處理器去創(chuàng)建的AOP的動態(tài)代理
網(wǎng)絡(luò)異常 取消重新上傳
如上圖. 我們在創(chuàng)建bean 的時候, 會有很多Bean的后置處理器BeanPostProcessor. 如果有AOP, 會在什么時候創(chuàng)建呢? 在初始化以后, 調(diào)用BeanPostProcessor創(chuàng)建動態(tài)代理.
結(jié)合上面的代碼, 我們想一想, 其實在初始化以后創(chuàng)建動態(tài)代理就晚了. 為什么呢? 因為, 如果有循環(huán)依賴, 在初始化之后才調(diào)用, 那就不是動態(tài)代理. 其實我們這時候應(yīng)該在實例化之后, 放入到二級緩存之前調(diào)用
面試題: 在創(chuàng)建bean的時候, 在哪里創(chuàng)建的動態(tài)代理, 這個應(yīng)該怎么回答呢?很多人會說在初始化之后, 或者在實例化之后.其實更嚴(yán)謹(jǐn)?shù)恼f, 有兩種情況: 第一種是在初始化之后調(diào)用 . 第二種是出現(xiàn)了循環(huán)依賴, 會在實例化之后調(diào)用
我們上面說的就是第二種情況. 也就是說,正常情況下是在初始化之后調(diào)用的, 但是如果有循環(huán)依賴, 就要在實例化之后調(diào)用了.
下面來看看如何在二級緩存加動態(tài)代理.
首先, 我們這里有循環(huán)依賴, 所以將動態(tài)代理放在實例化之后,
網(wǎng)絡(luò)異常 取消重新上傳
/**
? ? * 獲取bean, 根據(jù)beanName獲取? ? */
? ? public static Object getBean(String beanName) throws Exception {? ? ? ? // 增加一個出口. 判斷實體類是否已經(jīng)被加載過了
? ? ? ? Object singleton = getSingleton(beanName);? ? ? ? if (singleton != null) {? ? ? ? ? ? return singleton;
? ? ? ? }? ? ? ? /**
? ? ? ? * 第一步: 實例化
? ? ? ? * 我們這里是模擬, 采用反射的方式進(jìn)行實例化. 調(diào)用的也是最簡單的無參構(gòu)造函數(shù)? ? ? ? */
? ? ? ? RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
? ? ? ? Class<?> beanClass = beanDefinition.getBeanClass();? ? ? ? // 調(diào)用無參的構(gòu)造函數(shù)進(jìn)行實例化
? ? ? ? Object instanceBean = beanClass.newInstance();? ? ? ? /**
? ? ? ? * 創(chuàng)建AOP動態(tài)代理 (不是耦合的, 采用解耦的, 通過BeanPostProcessor bean的后置處理器得來的.? 之前講過,
? ? ? ? * 在初始化之后, 調(diào)用Bean的后置處理器去創(chuàng)建的AOP的動態(tài)代理 )
? ? ? ? */
? ? ? ? instanceBean = new JdkProxyBeanPostProcessor().getEarlyBeanReference(instanceBean, "instanceA");? ? ? ? /**
? ? ? ? * 第二步: 放入到二級緩存? ? ? ? */
? ? ? ? earlySingletonObjects.put(beanName, instanceBean);? ? ? ? /**
? ? ? ? *? 第三步: 屬性賦值
? ? ? ? *? instanceA這類類里面有一個屬性, InstanceB. 所以, 先拿到 instanceB, 然后在判斷屬性頭上有沒有Autowired注解.
? ? ? ? *? 注意: 這里我們只是判斷有沒有Autowired注解. spring中還會判斷有沒有@Resource注解. @Resource注解還有兩種方式, 一種是name, 一種是type? ? ? ? ? */
? ? ? ? Field[] declaredFields = beanClass.getDeclaredFields();? ? ? ? for (Field declaredField: declaredFields) {? ? ? ? ? ? // 判斷每一個屬性是否有@Autowired注解
? ? ? ? ? ? Autowired annotation = declaredField.getAnnotation(Autowired.class);? ? ? ? ? ? if (annotation != null) {? ? ? ? ? ? ? ? // 設(shè)置這個屬性是可訪問的
? ? ? ? ? ? ? ? declaredField.setAccessible(true);? ? ? ? ? ? ? ? // 那么這個時候還要構(gòu)建這個屬性的bean.
? ? ? ? ? ? ? ? /*
? ? ? ? ? ? ? ? * 獲取屬性的名字
? ? ? ? ? ? ? ? * 真實情況, spring這里會判斷, 是根據(jù)名字, 還是類型, 還是構(gòu)造函數(shù)來獲取類.
? ? ? ? ? ? ? ? * 我們這里模擬, 所以簡單一些, 直接根據(jù)名字獲取.? ? ? ? ? ? ? ? */
? ? ? ? ? ? ? ? String name = declaredField.getName();? ? ? ? ? ? ? ? /**
? ? ? ? ? ? ? ? * 這樣, 在這里我們就拿到了 instanceB 的 bean? ? ? ? ? ? ? ? */
? ? ? ? ? ? ? ? Object fileObject = getBean(name);? ? ? ? ? ? ? ? // 為屬性設(shè)置類型
? ? ? ? ? ? ? ? declaredField.set(instanceBean, fileObject);
? ? ? ? ? ? }
? ? ? ? }? ? ? ? /**
? ? ? ? * 第四步: 初始化
? ? ? ? * 初始化就是設(shè)置類的init-method.這個可以設(shè)置也可以不設(shè)置. 我們這里就不設(shè)置了? ? ? ? */ ? //? 正常動態(tài)代理創(chuàng)建的時機(jī)
? ? ? ? /**
? ? ? ? * 第五步: 放入到一級緩存? ? ? ? */
? ? ? ? singletonObjects.put(beanName, instanceBean);? ? ? ? return instanceBean;
? ? }
網(wǎng)絡(luò)異常 取消重新上傳
這里只是簡單模擬了動態(tài)代理.
我們知道動態(tài)代理有兩個地方. 如果是普通類動態(tài)代理在初始化之后執(zhí)行, 如果是循環(huán)依賴, 那么動態(tài)代理是在實例化之后.
上面在實例化之后創(chuàng)建proxy的代碼不完整, 為什么不完整呢, 因為沒有判斷是否是循環(huán)依賴.
我們簡單模擬一個動態(tài)代理的實現(xiàn).
網(wǎng)絡(luò)異常 取消重新上傳
public class JdkProxyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {? ? /**
? ? * 假設(shè)A被切點(diǎn)命中 需要創(chuàng)建代理? @PointCut("execution(* *..InstanceA.*(..))")
? ? * @param bean the raw bean instance
? ? * @param beanName the name of the bean
? ? * @return
? ? * @throws BeansException? ? */
? ? @Override? ? public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {? ? ? ? // 假設(shè)A被切點(diǎn)命中 需要創(chuàng)建代理? @PointCut("execution(* *..InstanceA.*(..))")
? ? ? ? /**
? ? ? ? * 這里, 我們簡單直接判斷bean是不是InstanceA實例, 如果是, 就創(chuàng)建動態(tài)代理.
? ? ? ? * 這里沒有去解析切點(diǎn), 解析切點(diǎn)是AspectJ做的事.
? ? ? ? */
? ? ? ? if (bean instanceof InstanceA) {
? ? ? ? ? ? JdkDynimcProxy jdkDynimcProxy = new JdkDynimcProxy(bean);? ? ? ? ? ? return jdkDynimcProxy.getProxy();
? ? ? ? }? ? ? ? return bean;
? ? }
}
網(wǎng)絡(luò)異常 取消重新上傳
這里直接判斷, 如果bean是InstanceA的實例, 那么就調(diào)用bean的動態(tài)代理.? 動態(tài)代理的簡單邏輯就是: 解析切面, 然后創(chuàng)建類, 如果類不存在就新增, 如果存在則不在創(chuàng)建, 直接取出來返回.
在來看看動態(tài)代理,放在實例化之后. 創(chuàng)建AOP, 但是, 在這里創(chuàng)建AOP動態(tài)代理的條件是循環(huán)依賴.
問題1: 那么如何判斷是循環(huán)依賴呢?
二級緩存中bean不是null.
如果一個類在創(chuàng)建的過程中, 會放入到二級緩存, 如果完全創(chuàng)建完了, 會放入到一級緩存, 然后刪除二級緩存. 所以, 如果二級緩存中的bean只要存在, 就說明這個類是創(chuàng)建中, 出現(xiàn)了循環(huán)依賴.
問題2: 什么時候判斷呢?
應(yīng)該在getSingleton()判斷是否是循環(huán)依賴的時候判斷. 因為這時候我們剛好判斷了二級緩存中bean是否為空.
網(wǎng)絡(luò)異常 取消重新上傳
/**
? ? * 判斷是否是循環(huán)引用的出口.
? ? * @param beanName
? ? * @return? ? */
? ? private static Object getSingleton(String beanName) {? ? ? ? // 先去一級緩存里拿,如果一級緩存沒有拿到,去二級緩存里拿
? ? ? ? if (singletonObjects.containsKey(beanName)) {? ? ? ? ? ? return singletonObjects.get(beanName);
? ? ? ? } else if (earlySingletonObjects.containsKey(beanName)){? ? ? ? ? ? /**
? ? ? ? ? ? * 第一次創(chuàng)建bean是正常的instanceBean. 他并不是循環(huán)依賴. 第二次進(jìn)來判斷, 這個bean已經(jīng)存在了, 就說明是循環(huán)依賴了
? ? ? ? ? ? * 這時候通過動態(tài)代理創(chuàng)建bean. 然后將這個bean在放入到二級緩存中覆蓋原來的instanceBean.
? ? ? ? ? ? */
? ? ? ? ? ? Object obj = new JdkProxyBeanPostProcessor()
? ? ? ? ? ? ? ? ? ? .getEarlyBeanReference(earlySingletonObjects.get(beanName), beanName);
? ? ? ? ? ? earlySingletonObjects.put(beanName, obj);? ? ? ? ? ? return earlySingletonObjects.get(beanName);
? ? ? ? } else {? ? ? ? ? ? return null;
? ? ? ? }
? ? }
網(wǎng)絡(luò)異常 取消重新上傳
這樣我們在循環(huán)依賴的時候就完成了AOP的創(chuàng)建. 這是在二級緩存里創(chuàng)建的AOP,
問題3: 那這是不是說就不需要三級緩存了呢?
那么,艾曉園(https://www.xiaoyuani.com/)來找問題.? 這里有兩個問題:
問題1:我們發(fā)現(xiàn)在創(chuàng)建動態(tài)代理的時候, 我們使用的bean的后置處理器JdkProxyBeanPostProcessor.這有點(diǎn)不太符合規(guī)則,? ? ? 因為, spring在getBean()的時候并沒有使用Bean的后置處理器, 而是在createBean()的時候才去使用的bean的后置處理器.
問題2:如果A是AOP, 他一直都是, 最開始創(chuàng)建的時候也應(yīng)該是. 使用這種方法, 結(jié)果是第一次創(chuàng)建出來的bean不是AOP動態(tài)代理.
對于第一個問題: 我們希望在實例化的時候創(chuàng)建AOP, 但是具體判斷是在getSingleton()方法里判斷. 這里通過三級緩存來實現(xiàn). 三級緩存里面放的是一個接口定義的鉤子方法. 方法的執(zhí)行在后面調(diào)用的時候執(zhí)行.
對于第二個問題: 我們的二級緩存就不能直接保存instanceBean實例了, 增加一個參數(shù), 用來標(biāo)記當(dāng)前這個類是一個正在創(chuàng)建中的類. 這樣來判斷循環(huán)依賴.
下面先來看看創(chuàng)建的三個緩存和一個標(biāo)識
網(wǎng)絡(luò)異常 取消重新上傳
// 一級緩存
? ? private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();? ? // 二級緩存: 為了將成熟的bean和純凈的bean分離. 避免讀取到不完整的bean.
? ? private static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();? ? // 三級緩存:
? ? private static Map<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>();
? ? // 循環(huán)依賴的標(biāo)識---當(dāng)前正在創(chuàng)建的實例bean
? ? private static Set<String> singletonsCurrectlyInCreation = new HashSet<>();
網(wǎng)絡(luò)異常 取消重新上傳
然后在來看看循環(huán)依賴的出口
網(wǎng)絡(luò)異常 取消重新上傳
/**
? ? * 判斷是否是循環(huán)引用的出口.
? ? * @param beanName
? ? * @return? ? */
? ? private static Object getSingleton(String beanName) {? ? ? ? //先去一級緩存里拿
? ? ? ? Object bean = singletonObjects.get(beanName);? ? ? ? // 一級緩存中沒有, 但是正在創(chuàng)建的bean標(biāo)識中有, 說明是循環(huán)依賴
? ? ? ? if (bean == null && singletonsCurrectlyInCreation.contains(beanName)) {
? ? ? ? ? ? bean = earlySingletonObjects.get(beanName);? ? ? ? ? ? // 如果二級緩存中沒有, 就從三級緩存中拿
? ? ? ? ? ? if (bean == null) {? ? ? ? ? ? ? ? // 從三級緩存中取
? ? ? ? ? ? ? ? ObjectFactory objectFactory = singletonFactories.get(beanName);? ? ? ? ? ? ? ? if (objectFactory != null) {? ? ? ? ? ? ? ? ? ? // 這里是真正創(chuàng)建動態(tài)代理的地方.
? ? ? ? ? ? ? ? ? ? Object obj = objectFactory.getObject();? ? ? ? ? ? ? ? ? ? // 然后將其放入到二級緩存中. 因為如果有多次依賴, 就去二級緩存中判斷. 已經(jīng)有了就不在再次創(chuàng)建了? ? ? ? ? ? ? ? ? ? earlySingletonObjects.put(beanName, obj);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }? ? ? ? return bean;
? ? }
網(wǎng)絡(luò)異常 取消重新上傳
這里的邏輯是, 先去一級緩存中拿, 一級緩存放的是成熟的bean, 也就是他已經(jīng)完成了屬性賦值和初始化. 如果一級緩存沒有, 而正在創(chuàng)建中的類標(biāo)識是true, 就說明這個類正在創(chuàng)建中, 這是一個循環(huán)依賴. 這個時候就去二級緩存中取數(shù)據(jù), 二級緩存中的數(shù)據(jù)是何時放進(jìn)去的呢, 是后面從三級緩存中創(chuàng)建動態(tài)代理后放進(jìn)去的. 如果二級緩存為空, 說明沒有創(chuàng)建過動態(tài)代理, 這時候在去三級緩存中拿, 然后創(chuàng)建動態(tài)代理. 創(chuàng)建完以后放入二級緩存中, 后面就不用再創(chuàng)建.
完成的代碼如下:
網(wǎng)絡(luò)異常 取消重新上傳
package com.lxl.www.circulardependencies;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;public class MainStart {? ? private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();? ? // 一級緩存
? ? private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();? ? // 二級緩存: 為了將成熟的bean和純凈的bean分離. 避免讀取到不完整的bean.
? ? private static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();? ? // 三級緩存:
? ? private static Map<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>();? ? // 循環(huán)依賴的標(biāo)識---當(dāng)前正在創(chuàng)建的實例bean
? ? private static Set<String> singletonsCurrectlyInCreation = new HashSet<>();? ? /**
? ? * 讀取bean定義, 當(dāng)然在spring中肯定是根據(jù)配置 動態(tài)掃描注冊的
? ? *
? ? * InstanceA和InstanceB都有注解@Component, 所以, 在spring掃描讀取配置類的時候, 會把他們兩個掃描到BeanDefinitionMap中.
? ? * 這里, 我們省略這一步, 直接將instanceA和instanceB放到BeanDefinitionMap中.? ? */
? ? public static void loadBeanDefinitions(){
? ? ? ? RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class);
? ? ? ? RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class);
? ? ? ? beanDefinitionMap.put("instanceA", aBeanDefinition);
? ? ? ? beanDefinitionMap.put("instanceB", bBeanDefinition);
? ? }? ? public static void main(String[] args) throws Exception {? ? ? ? // 第一步: 掃描配置類, 讀取bean定義? ? ? ? loadBeanDefinitions();? ? ? ? // 第二步: 循環(huán)創(chuàng)建bean
? ? ? ? for (String key: beanDefinitionMap.keySet()) {? ? ? ? ? ? // 第一次: key是instanceA, 所以先創(chuàng)建A類? ? ? ? ? ? getBean(key);
? ? ? ? }? ? ? ? // 測試: 看是否能執(zhí)行成功
? ? ? ? InstanceA instanceA = (InstanceA) getBean("instanceA");
? ? ? ? instanceA.say();
? ? }? ? /**
? ? * 獲取bean, 根據(jù)beanName獲取? ? */
? ? public static Object getBean(String beanName) throws Exception {? ? ? ? // 增加一個出口. 判斷實體類是否已經(jīng)被加載過了
? ? ? ? Object singleton = getSingleton(beanName);? ? ? ? if (singleton != null) {? ? ? ? ? ? return singleton;
? ? ? ? }? ? ? ? // 標(biāo)記bean正在創(chuàng)建
? ? ? ? if (!singletonsCurrectlyInCreation.contains(beanName)) {
? ? ? ? ? ? singletonsCurrectlyInCreation.add(beanName);
? ? ? ? }? ? ? ? /**
? ? ? ? * 第一步: 實例化
? ? ? ? * 我們這里是模擬, 采用反射的方式進(jìn)行實例化. 調(diào)用的也是最簡單的無參構(gòu)造函數(shù)? ? ? ? */
? ? ? ? RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
? ? ? ? Class<?> beanClass = beanDefinition.getBeanClass();? ? ? ? // 調(diào)用無參的構(gòu)造函數(shù)進(jìn)行實例化
? ? ? ? Object instanceBean = beanClass.newInstance();? ? ? ? /**
? ? ? ? * 第二步: 放入到三級緩存
? ? ? ? * 每一次createBean都會將其放入到三級緩存中. getObject是一個鉤子方法. 在這里不會被調(diào)用.
? ? ? ? * 什么時候被調(diào)用呢?
? ? ? ? * 在getSingleton()從三級緩存中取數(shù)據(jù), 調(diào)用創(chuàng)建動態(tài)代理的時候? ? ? ? */
? ? ? ? singletonFactories.put(beanName, new ObjectFactory() {
? ? ? ? ? ? @Override? ? ? ? ? ? public Object getObject() throws BeansException {? ? ? ? ? ? ? ? return new JdkProxyBeanPostProcessor().getEarlyBeanReference(earlySingletonObjects.get(beanName), beanName);
? ? ? ? ? ? }
? ? ? ? });? ? ? ? //earlySingletonObjects.put(beanName, instanceBean);
? ? ? ? /**
? ? ? ? *? 第三步: 屬性賦值
? ? ? ? *? instanceA這類類里面有一個屬性, InstanceB. 所以, 先拿到 instanceB, 然后在判斷屬性頭上有沒有Autowired注解.
? ? ? ? *? 注意: 這里我們只是判斷有沒有Autowired注解. spring中還會判斷有沒有@Resource注解. @Resource注解還有兩種方式, 一種是name, 一種是type? ? ? ? */
? ? ? ? Field[] declaredFields = beanClass.getDeclaredFields();? ? ? ? for (Field declaredField: declaredFields) {? ? ? ? ? ? // 判斷每一個屬性是否有@Autowired注解
? ? ? ? ? ? Autowired annotation = declaredField.getAnnotation(Autowired.class);? ? ? ? ? ? if (annotation != null) {? ? ? ? ? ? ? ? // 設(shè)置這個屬性是可訪問的
? ? ? ? ? ? ? ? declaredField.setAccessible(true);? ? ? ? ? ? ? ? // 那么這個時候還要構(gòu)建這個屬性的bean.
? ? ? ? ? ? ? ? /*
? ? ? ? ? ? ? ? * 獲取屬性的名字
? ? ? ? ? ? ? ? * 真實情況, spring這里會判斷, 是根據(jù)名字, 還是類型, 還是構(gòu)造函數(shù)來獲取類.
? ? ? ? ? ? ? ? * 我們這里模擬, 所以簡單一些, 直接根據(jù)名字獲取.? ? ? ? ? ? ? ? */
? ? ? ? ? ? ? ? String name = declaredField.getName();? ? ? ? ? ? ? ? /**
? ? ? ? ? ? ? ? * 這樣, 在這里我們就拿到了 instanceB 的 bean? ? ? ? ? ? ? ? */
? ? ? ? ? ? ? ? Object fileObject = getBean(name);? ? ? ? ? ? ? ? // 為屬性設(shè)置類型
? ? ? ? ? ? ? ? declaredField.set(instanceBean, fileObject);
? ? ? ? ? ? }
? ? ? ? }? ? ? ? /**
? ? ? ? * 第四步: 初始化
? ? ? ? * 初始化就是設(shè)置類的init-method.這個可以設(shè)置也可以不設(shè)置. 我們這里就不設(shè)置了? ? ? ? */
? ? ? ? /**
? ? ? ? * 第五步: 放入到一級緩存
? ? ? ? *
? ? ? ? * 在這里二級緩存存的是動態(tài)代理, 那么一級緩存肯定也要存動態(tài)代理的實例.
? ? ? ? * 從二級緩存中取出實例, 放入到一級緩存中? ? ? ? */
? ? ? ? if (earlySingletonObjects.containsKey(beanName)) {
? ? ? ? ? ? instanceBean = earlySingletonObjects.get(beanName);
? ? ? ? }
? ? ? ? singletonObjects.put(beanName, instanceBean);? ? ? ? return instanceBean;
? ? }? ? /**
? ? * 判斷是否是循環(huán)引用的出口.
? ? * @param beanName
? ? * @return
? ? */
? ? private static Object getSingleton(String beanName) {? ? ? ? //先去一級緩存里拿,
? ? ? ? Object bean = singletonObjects.get(beanName);? ? ? ? // 一級緩存中沒有, 但是正在創(chuàng)建的bean標(biāo)識中有, 說明是循環(huán)依賴
? ? ? ? if (bean == null && singletonsCurrectlyInCreation.contains(beanName)) {
? ? ? ? ? ? bean = earlySingletonObjects.get(beanName);? ? ? ? ? ? // 如果二級緩存中沒有, 就從三級緩存中拿
? ? ? ? ? ? if (bean == null) {? ? ? ? ? ? ? ? // 從三級緩存中取
? ? ? ? ? ? ? ? ObjectFactory objectFactory = singletonFactories.get(beanName);? ? ? ? ? ? ? ? if (objectFactory != null) {? ? ? ? ? ? ? ? ? ? // 這里是真正創(chuàng)建動態(tài)代理的地方.
? ? ? ? ? ? ? ? ? ? Object obj = objectFactory.getObject();? ? ? ? ? ? ? ? ? ? // 然后將其放入到二級緩存中. 因為如果有多次依賴, 就去二級緩存中判斷. 已經(jīng)有了就不在再次創(chuàng)建了? ? ? ? ? ? ? ? ? ? earlySingletonObjects.put(beanName, obj);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }? ? ? ? return bean;
? ? }}
網(wǎng)絡(luò)異常 取消重新上傳
網(wǎng)絡(luò)異常 取消重新上傳
下面就我們的代碼分析一下:
第一種情況: 沒有循環(huán)依賴
第二種情況: 有循環(huán)依賴
第三種情況: 有多次循環(huán)依賴
我們模擬一個循環(huán)依賴的場景, 覆蓋這三種情況.
網(wǎng)絡(luò)異常 取消重新上傳
用代碼表示
類A
網(wǎng)絡(luò)異常 取消重新上傳
package com.lxl.www.circulardependencies;
import org.springframework.beans.factory.annotation.Autowired;public class A {
? ? @Autowired? ? private B b;
? ? @Autowired? ? private C c;
}
網(wǎng)絡(luò)異常 取消重新上傳
類B
網(wǎng)絡(luò)異常 取消重新上傳
package com.lxl.www.circulardependencies;
import org.springframework.beans.factory.annotation.Autowired;public class B {
? ? @Autowired? ? private A a;
? ? @Autowired? ? private B b;
}
網(wǎng)絡(luò)異常 取消重新上傳
類C
網(wǎng)絡(luò)異常 取消重新上傳
package com.lxl.www.circulardependencies;
import org.springframework.beans.factory.annotation.Autowired;public class C {
? ? @Autowired? ? private A a;
}
網(wǎng)絡(luò)異常 取消重新上傳
其中類A剛好匹配AOP的切面@PointCut("execution(* *..A.*(..))")
下面分析他們的循環(huán)依賴關(guān)系.
此時beanDefinitionMap中有三個bean定義. 分別是A, B, C
1. 先解析類A, 根據(jù)上面的流程.
1) 首先調(diào)用getSingleton, 此時一級緩存, 二級緩存都沒有, 正在創(chuàng)建標(biāo)志也是null. 所以, 返回的是null
2) 標(biāo)記當(dāng)前類正在創(chuàng)建中
3) 實例化
4) 將A放入到三級緩存, 并定義動態(tài)代理的鉤子方法
5) 屬性賦值. A有兩個屬性, 分別是B和C. 都帶有@Autowired注解, 先解析B.
6) A暫停, 解析B
2. 解析A類的屬性類B
1) 首先調(diào)用getSingleton, 此時一級緩存, 二級緩存都沒有, 正在創(chuàng)建標(biāo)志也是null. 所以, 返回的是null
2) 標(biāo)記當(dāng)前類正在創(chuàng)建中
3) 實例化
4) 將B放入到三級緩存, 并定義動態(tài)代理的鉤子方法
5) 屬性賦值. B有兩個屬性, 分別是A和C. 都帶有@Autowired注解, 先解析A. 在解析C
6) B暫停, 解析A
3. 解析B類的屬性A
1) 首先調(diào)用getSingleton, 此時一級緩存中這個屬性為null, 正在創(chuàng)建中標(biāo)志位true, 二級緩存為空, 從三級緩存中創(chuàng)建動態(tài)代理,然后判斷是否符合動態(tài)代理切面要求, A符合. 所以通過動態(tài)代理創(chuàng)建A的代理bean放入到二級緩存.返回實例bean.
2) A此時已經(jīng)存在了, 所以, 直接返回
4. 解析B類的屬性C
1) 首先調(diào)用getSingleton, 此時一級緩存, 二級緩存都沒有, 正在創(chuàng)建標(biāo)志也是null. 所以, 返回的是null
2) 標(biāo)記當(dāng)前類C正在創(chuàng)建中
3) 實例化
4) 將C放入到三級緩存, 并定義動態(tài)代理的鉤子方法
5) 屬性賦值. C有一個屬性, 是A. 帶有@Autowired注解, 先解析A
6) C暫停, 解析A
5. 解析C中的屬性A
1) 首先調(diào)用getSingleton()方法, 此時一級緩存中沒有, 標(biāo)志位為true, 二級緩存中已經(jīng)有A的動態(tài)代理實例了, 所以,直接返回.
2) A此時已經(jīng)在存在, 直接返回
6. 繼續(xù)解析B類的屬性C
1) 接著第4步往下走
2) 初始化類C
3)將類C放入到一級緩存中.放之前去二級緩存中取, 二級緩存中沒有. 所以, 這里存的是C通過反射構(gòu)建的instanceBean
7. 繼續(xù)解析A類的屬性類B
1) 接著第2步往下走
2) 初始化類B
3) 將類B放入到一級緩存中. 放之前去二級緩存中取.二級緩存中沒有, 所以, 這里存的是B通過反射構(gòu)建的instanceBean
4) 構(gòu)建結(jié)束,返回
8. 解析A類的屬性類C
1) 首先調(diào)用getSingleton()方法, 此時一級緩存中已經(jīng)有了類C, 所以直接返回
9. 繼續(xù)解析A類
1) 接著第1步往下走
2) 初始化類A
3) 將A放入到一級緩存中. 放之前判斷二級緩存中有沒有實例bean, 我們發(fā)現(xiàn)有, 所以, 取出來放入到A的一級緩存中.
4) 構(gòu)建bean結(jié)束, 返回
10. 接下來構(gòu)建beanDefinitionMap中的類B
1) 首先調(diào)用getSingleton()方法, 此時一級緩存中已經(jīng)有了類B, 所以直接返回
11. 接下來構(gòu)建beanDefinitionMap中的類C
1) 首先調(diào)用getSingleton()方法, 此時一級緩存中已經(jīng)有了類C, 所以直接返回
至此整個構(gòu)建過程結(jié)束.
總結(jié):
再來感受一下三級緩存的作用:
一級緩存:用來存放成熟的bean. 這個bean如果是切入點(diǎn), 則是一個動態(tài)代理的bean,如果不是切入點(diǎn), 則是一個普通的類
二級緩存:用來存放循環(huán)依賴過程中創(chuàng)建的動態(tài)代理bean.
三級緩存:用來存放動態(tài)代理的鉤子方法. 用來在需要構(gòu)建動態(tài)代理類的時候使用.