Spring FactoryBean 缓存

Posted CoderLi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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>

技术图片

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

技术图片

技术图片

以上是关于Spring FactoryBean 缓存的主要内容,如果未能解决你的问题,请参考以下文章

Spring源码分析(十三)缓存中获取单例bean

spring FactoryBean配置Bean

Spring中Bean的配置方式之FactoryBean

Spring源码分析FactoryBean的使用

缓存中获取单例bean

Spring系列:@FactoryBean注解用法介绍