相關文章
- Spring 整體架構
- 編譯Spring5.2.0源碼
- Spring-資源加載
- Spring 容器的初始化
- Spring-AliasRegistry
- Spring 獲取單例流程(一)
- Spring 獲取單例流程(二)
- Spring 獲取單例流程(三)
- Spring 循環依賴
- …
讀完這篇文章你將會收穫到
DisposableBean
的destroy
執行Spring
如何緩存FactoryBean
產生的單例bean
- 如何解決
FctoryBean
的getObject
的循環依賴
不多BB , 上圖
SingletonBeanRegistry
今天我們來扯一下 SingletonBeanRegistry
, 我們向 Spring
容器註冊 bean
的時候就是用到這個接口的方法
public interface SingletonBeanRegistry {
void registerSingleton(String beanName, Object singletonObject);
@Nullable
Object getSingleton(String beanName);
boolean containsSingleton(String beanName);
String[] getSingletonNames();
int getSingletonCount();
// 併發控制 現在的實現都是 使用 第一級緩存的 singletonObjects 對象
Object getSingletonMutex();
}
DefaultSingletonBeanRegistry
我們先看看這個接口的默認實現類 DefaultSingletonBeanRegistry
我們前幾篇文章說的三級緩存就是在這裏定義的
/**
* 第一級緩存
*/
Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* 第三級緩存
*/
Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/**
* 第二級緩存
**/
Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/**
* 只要是加入到三級緩存中到 beanName 都會被註冊到這個 set 無論是第三級緩存或者是第一級緩存
*/
Set<String> registeredSingletons = new LinkedHashSet<>(256);
/**
* Names of beans that are currently in creation.
* 正在處於一個創建狀態的 bean、存放的是 beanName
*/
Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/**
* 不用在創建的時候檢查循環依賴的 beanName 名稱
*/
Set<String> inCreationCheckExclusions =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/**
* 收集一些並不影響主流程的異常,可用於後續再次拋出的時候做一些關聯,或者只是收集而不拋出
*/
@Nullable
Set<Exception> suppressedExceptions;
/**
* 表明是否正處於一個正在銷燬 singleton 的過程
*/
boolean singletonsCurrentlyInDestruction = false;
/**
* beanName:需要執行destroyMethod 的bean
*/
Map<String, Object> disposableBeans = new LinkedHashMap<>();
/**
* * key: 外部的 beanName
* value 外部 bean 依賴的一些內部 bean
*/
Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
/**
* key bean name
* value 所有依賴 key的 bean
*/
Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
/**
* key bean name
* value 這個key 所依賴的bean
*/
Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
上面除了三級緩存以外,還有其他一些屬性定義
getSingletonMutex
我們再來看看其如何實現 SingletonBeanRegistry
接口的方法
@Override
public final Object getSingletonMutex() {
return this.singletonObjects;
}
返回第一級緩存這個 Map
作爲同步鎖的對象,子類需要使用 synchronized
的時候就要獲取這個同步鎖對象
registerSingleton
@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
Assert.notNull(beanName, "Bean name must not be null");
Assert.notNull(singletonObject, "Singleton object must not be null");
synchronized (this.singletonObjects) {
Object oldObject = this.singletonObjects.get(beanName);
if (oldObject != null) {
throw new IllegalStateException("xxxxx");
}
addSingleton(beanName, singletonObject);
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 加入到第一級緩存
this.singletonObjects.put(beanName, singletonObject);
// 從第三級緩存中移除
this.singletonFactories.remove(beanName);
// 從第二級緩存中移除
this.earlySingletonObjects.remove(beanName);
// 註冊這個beanName
this.registeredSingletons.add(beanName);
}
}
先是對參數的檢驗、然後使用同步鎖,再判斷該 beanName
是否已經在第一級緩存中、如果已經存在了、則拋出異常,不管在緩存中的 bean
是否跟參數中的 singletonObject
是同一個對象
然後加入到第一級緩存中並註冊這個 beanName
、然後從第二級第三級緩存中移除這個 beanName
getSingleton
@Override
@Nullable
public Object getSingleton(String beanName) {
// allowEarlyReference 可以返回第三級緩存的對象
return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
// 這個bean 正處於 創建階段
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 併發控制
synchronized (this.singletonObjects) {
// 單例緩存是否存在
singletonObject = this.earlySingletonObjects.get(beanName);
// 是否運行獲取 bean factory 創建出的 bean
if (singletonObject == null && allowEarlyReference) {
// 獲取緩存中的 ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 將對象緩存到 earlySingletonObject中
this.earlySingletonObjects.put(beanName, singletonObject);
// 從工廠緩衝中移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
上面這個方法我們已經見過了很多次 , 分別從第一級而後第二級、再到第三級獲取 bean
、如果從第三級的話、那麼就將其升級到第二級並從第三級移除。
其他
下面這三個方法就比較簡單了、不做介紹了、相信大家都能看一眼就知道幹啥了
@Override
public boolean containsSingleton(String beanName) {
return this.singletonObjects.containsKey(beanName);
}
@Override
public String[] getSingletonNames() {
synchronized (this.singletonObjects) {
return StringUtils.toStringArray(this.registeredSingletons);
}
}
@Override
public int getSingletonCount() {
synchronized (this.singletonObjects) {
return this.registeredSingletons.size();
}
}
我們再來看一個往第三級緩存中存放 ObjectFactory
的實現
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
我們再來看看註冊 DisposableBean
的方法吧
public void registerDisposableBean(String beanName, DisposableBean bean) {
synchronized (this.disposableBeans) {
this.disposableBeans.put(beanName, bean);
}
}
而執行 destroy
的話內容比較長
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
Set<String> dependencies;
synchronized (this.dependentBeanMap) {
// 找出所有依賴這個 beanName 的其他 bean
dependencies = this.dependentBeanMap.remove(beanName);
}
if (dependencies != null) {
for (String dependentBeanName : dependencies) {
// 最終還是回調到這個方法
destroySingleton(dependentBeanName);
}
}
// 如果 DisposableBean 不爲null
if (bean != null) {
try {
bean.destroy();
} catch (Throwable ex) {
}
}
// 處理內部 bean 了
Set<String> containedBeans;
synchronized (this.containedBeanMap) {
// 找出這個 beanName 的所有內部 bean
containedBeans = this.containedBeanMap.remove(beanName);
}
if (containedBeans != null) {
for (String containedBeanName : containedBeans) {
destroySingleton(containedBeanName);
}
}
// dependentBeanMap key 爲被依賴者、value 爲依賴 key 的 bean
// 這一步的操作就是因爲 beanName 可能存在 別人的 value 中、這個時候我們也要去清理掉
// 第一步的時候已經清除了 key 爲 beanName 的情況
synchronized (this.dependentBeanMap) {
for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Set<String>> entry = it.next();
Set<String> dependenciesToClean = entry.getValue();
dependenciesToClean.remove(beanName);
if (dependenciesToClean.isEmpty()) {
it.remove();
}
}
}
// dependenciesForBeanMap key 爲依賴者,value 爲 key 依賴的 bean 集合
this.dependenciesForBeanMap.remove(beanName);
}
FactoryBeanRegistrySupport
我們在來看看這個類,這個類是幹啥的
當我們向 Spring
註冊的 bean
是 FactoryBean
的話、那麼這個 FactoryBean
當然是存放在三級緩存中啦、但是這個 FactoryBean
產生的 bean
也得有個地方緩存起來吧(如果是個單例的話,是吧)
/**
* beanName: bean(factory bean 創建出來的)
* Cache of singleton objects created by FactoryBeans: FactoryBean name to object.
*/
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
我們這裏介紹一個上幾篇文章說過的一個方法、但是其中的妙處、我現在算是看懂了
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
// 爲單例模式且 beanName 已經註冊了在 Spring 中
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// 從緩存中獲取指定的 bean(這個bean 是從 factory bean 創建出來的)
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 爲空則從 factory bean 中獲取對象
object = doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
// 已經存放到 緩存中了、後續的操作就不需要了
object = alreadyThere;
} else {
// 需要做一些後置處理
if (shouldPostProcess) {
// 如果這個bean正在創建中、
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily return non-post-processed object, not storing it yet..
return object;
}
// 前置處理 主要是將這個bean 加入到正在創建中的隊列 singletonsCurrentlyInCreation
beforeSingletonCreation(beanName);
try {
// 對 從 factoryBean 獲取的對象進行後處理
// 生成對象將暴露給 bean 引用 並回調 beanPostProcessor
object = postProcessObjectFromFactoryBean(object, beanName);
} catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
} finally {
// 後置處理 將其從 singletonsCurrentlyInCreation 移除
afterSingletonCreation(beanName);
}
}
// 他的 factory bean 已經存在 緩存中了、那麼這個 factory bean 產生的bean 應該也要緩存一下
if (containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
} else {
// 非單例
.......
}
}
其實有個挺讓人迷惑的一個地方
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 爲空則從 factory bean 中獲取對象
object = doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
// 已經存放到 緩存中了、後續的操作就不需要了
object = alreadyThere;
}
我上一步已經判斷了 factoryBeanObjectCache
裏面沒有這個 beanName
了,而 doGetObjectFromFactoryBean
這個方法只是單純的去調用 FactoryBean
裏面的 getObject
方法、並無其他操作,那麼爲啥 alreadyThere
它會有不爲 null
的情況呢 ?
我們先設定、FactoryBean
的 beanName
爲 beanA
吧,還有一個普通的 beanB
、它是依賴 beanA
的。
那麼假如我在 beanA
的 getObject
的方法裏面調用 getBean
方法獲取 beanB
, 這個時候就構成了一個循環依賴,當創建好 beanB
的時候、進行屬性注入,發現要 beanA
、這個時候就會繼續走上面的流程、也就是 alreadyThere == null
的情況、這個時候會將 beanA
放置到 factoryBeanObjectCache
中、最終創建好了 beanB
, 返回到 doGetObjectFromFactoryBean
這裏的方法、這個時候就會產生了兩個 beanA
(如果你正常的在 getObject``````new
某個對象的話) , 是不是就出現了 alreadyThere
不爲 null
的情況了
來個 demo
看看吧
public class CatFactoryBean implements FactoryBean<Cat>, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public Cat getObject() throws Exception {
beanFactory.getBean("chicken");
return new Cat();
}
@Override
public Class<?> getObjectType() {
return Cat.class;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
public class Chicken {
private Cat cat;
public Chicken() {
}
public void destroyMethod() {
System.out.println("destroy method");
}
public void setCat(Cat cat) {
this.cat = cat;
}
}
public static void main(String[] args) {
Resource resource = new ClassPathResource("coderLi.xml");
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions(resource);
Object cat = defaultListableBeanFactory.getBean("cat");
defaultListableBeanFactory.destroySingletons();
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.demo.data.Chicken" id="chicken" destroy-method="destroyMethod">
<property name="cat" ref="cat"/>
</bean>
<bean class="com.demo.data.CatFactoryBean" id="cat"/>
</beans>
好啦,希望你也有所收穫喔