Spring FactoryBean 緩存

相關文章web

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

  • 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); /**  * 正在處於一個建立狀態的 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 接口的方法ide

@Override
public final Object getSingletonMutex() {  return this.singletonObjects; } 複製代碼

返回第一級緩存這個 Map 做爲同步鎖的對象,子類須要使用 synchronized 的時候就要獲取這個同步鎖對象post

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 是同一個對象this

而後加入到第一級緩存中並註冊這個 beanName、而後從第二級第三級緩存中移除這個 beanNamespa

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
image-20200607175242277

好啦,但願你也有所收穫喔

此次必定?
此次必定?
相關文章
相關標籤/搜索