相關文章web
讀完這篇文章你將會收穫到spring
DisposableBean
的
destroy
執行
Spring
如何緩存
FactoryBean
產生的單例
bean
FctoryBean
的
getObject
的循環依賴
很少BB , 上圖緩存
今天咱們來扯一下 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
咱們前幾篇文章說的三級緩存就是在這裏定義的併發
/** * 第一級緩存 */ 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); /** * 正在處於一個建立狀態的 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); 複製代碼
上面除了三級緩存之外,還有其餘一些屬性定義編輯器
咱們再來看看其如何實現 SingletonBeanRegistry
接口的方法ide
@Override
public final Object getSingletonMutex() { return this.singletonObjects; } 複製代碼
返回第一級緩存這個 Map
做爲同步鎖的對象,子類須要使用 synchronized
的時候就要獲取這個同步鎖對象post
@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
是同一個對象this
而後加入到第一級緩存中並註冊這個 beanName
、而後從第二級第三級緩存中移除這個 beanName
spa
@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); } 複製代碼
咱們在來看看這個類,這個類是幹啥的
當咱們向 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> 複製代碼
好啦,但願你也有所收穫喔