- Spring Boot進(jìn)階:原理、實戰(zhàn)與面試題分析
- 鄭天民
- 846字
- 2022-07-05 09:41:43
2.3.1 三級緩存結(jié)構(gòu)
所謂的三級緩存,在Spring中表現(xiàn)為三個Map對象,如代碼清單2-17所示。這三個Map對象定義在DefaultSingletonBeanRegistry類中,該類是DefaultListableBeanFactory的父類。
代碼清單2-17 DefaultSingletonBeanRegistry中的三級緩存Map定義代碼
/** 單例對象的緩存: bean name --> bean instance */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** 單例對象工廠的緩存: bean name --> ObjectFactory */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /** 提前暴露的單例對象的緩存: bean name --> bean instance */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
請注意,這里的singletonObjects變量就是第一級緩存,用來持有完整的Bean實例。而earlySingletonObjects中存放的是那些提前暴露的對象,也就是已經(jīng)創(chuàng)建但還沒有完成屬性注入的對象,屬于第二級緩存。最后的singletonFactories存放用來創(chuàng)建earlySingleton-Objects的工廠對象,屬于第三級緩存。
那么,三級緩存是如何發(fā)揮作用的呢?讓我們來分析獲取Bean的代碼流程,如代碼清單2-18所示。
代碼清單2-18 獲取Bean的getSingleton()方法代碼
protected Object getSingleton(String beanName, boolean allowEarlyReference) { //首先從一級緩存singletonObjects中獲取 Object singletonObject = this.singletonObjects.get(beanName); //如果獲取不到,就從二級緩存earlySingletonObjects中獲取 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); //如果還是獲取不到,就從三級緩存singletonFactory中獲取 if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); //一旦獲取成功,就把對象從第三級緩存移動到第二級緩存中 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
看了這段代碼,我們不難理解對三級緩存的依次訪問過程,但可能還是不理解Spring為什么要這樣設(shè)計。事實上,解決循環(huán)依賴的關(guān)鍵還是要圍繞Bean的生命周期。在2.2.2節(jié)中介紹Bean的實例化時,我們知道它包含三個核心步驟,而在第一步和第二步之間,存在一個addSingletonFactory()方法,如代碼清單2-19所示。
代碼清單2-19 Bean實例化過程中的addSingletonFactory()方法代碼
//1. 初始化Bean,通過構(gòu)造函數(shù)創(chuàng)建Bean instanceWrapper = createBeanInstance(beanName, mbd, args); //針對循環(huán)依賴問題暴露單例工廠類 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); //2. 初始化Bean實例,完成Bean實例的完整創(chuàng)建 populateBean(beanName, mbd, instanceWrapper);
Spring解決循環(huán)依賴的訣竅就在于singletonFactories這個第三級緩存,上述addSingleton-Factory()方法用于初始化這個第三級緩存中的數(shù)據(jù),如代碼清單2-20所示。
代碼清單2-20 addSingletonFactory()方法代碼
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { //添加Bean到第三級緩存中 this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
請注意,這段代碼的執(zhí)行時機是在已經(jīng)通過構(gòu)造函數(shù)創(chuàng)建Bean,但還沒有完成對Bean中完整屬性的注入的時候。換句話說,Bean已經(jīng)可以被暴露出來進(jìn)行識別了,但還不能正常使用。接下來我們就來分析一下為什么通過這種機制就能解決循環(huán)依賴問題。
- 演進(jìn)式架構(gòu)(原書第2版)
- Deploying Node.js
- Learning Apex Programming
- Visual Basic編程:從基礎(chǔ)到實踐(第2版)
- Java從入門到精通(第4版)
- Spring Cloud、Nginx高并發(fā)核心編程
- TypeScript項目開發(fā)實戰(zhàn)
- Express Web Application Development
- Python算法指南:程序員經(jīng)典算法分析與實現(xiàn)
- jQuery技術(shù)內(nèi)幕:深入解析jQuery架構(gòu)設(shè)計與實現(xiàn)原理
- Learning Grunt
- ASP.NET開發(fā)寶典
- Flink入門與實戰(zhàn)
- C++17 By Example
- Professional JavaScript