Spring FactoryBean 緩存

相關文章

讀完這篇文章你將會收穫到

  • DisposableBeandestroy執行
  • Spring如何緩存 FactoryBean產生的單例 bean
  • 如何解決 FctoryBeangetObject的循環依賴

不多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註冊的 beanFactoryBean的話、那麼這個 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的情況呢 ?

我們先設定、FactoryBeanbeanNamebeanA吧,還有一個普通的 beanB、它是依賴 beanA的。

那麼假如我在 beanAgetObject的方法裏面調用 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>

image-20200607175242277

好啦,希望你也有所收穫喔

這次一定?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章