java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > spring三级缓存的解读

关于spring三级缓存的解读

作者:ambity_lyf

Spring三级缓存解决循环依赖、AOP和多线程问题,包括singletonObjects、earlySingletonObjects和singletonFactories三层缓存,通过不同方法获取bean并解决这些问题

spring三级缓存的解读

spring 中为了解决 B的重复利用,A 依赖B 的循环依赖,aop 问题,多线程可能拿到不完整的bean 的问题引入了3层缓存,分别是

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

从3级缓存中 获取bean 的方法有如下两个

1.从缓存中获取,主要用来解决循环依赖问题

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   // 这里不用考虑指令重排,因为 bean 初始化完成后才会放入map
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      // 加锁,与另一个getSingleton 方法 互斥访问,保证并发情况下获取到不完整的bean
      synchronized (this.singletonObjects) {
         singletonObject = this.earlySingletonObjects.get(beanName);
         if (singletonObject == null && allowEarlyReference) {
            // 获取工厂bean ,因为可能是aop 生成的代理,这里二级和三级缓存保证获取到的bean 是最后的完整bean
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();
               // 放入 二级缓存中,从3级缓存中移除
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}

2.创建bean 的真正逻辑

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "Bean name must not be null");
   synchronized (this.singletonObjects) {
      // 再次从一级缓存中获取,因为多线程加载同一个bean 时,
      //相当于 双重检查
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
         if (this.singletonsCurrentlyInDestruction) {
            throw new BeanCreationNotAllowedException(beanName,
                  "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                  "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
         }
         if (logger.isDebugEnabled()) {
            logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
         }
         // 标记bean 正在创建
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
         }
         try {
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         }
         catch (IllegalStateException ex) {
            // Has the singleton object implicitly appeared in the meantime ->
            // if yes, proceed with it since the exception indicates that state.
            singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
               throw ex;
            }
         }
         catch (BeanCreationException ex) {
            if (recordSuppressedExceptions) {
               for (Exception suppressedException : this.suppressedExceptions) {
                  ex.addRelatedCause(suppressedException);
               }
            }
            throw ex;
         }
         finally {
            if (recordSuppressedExceptions) {
               this.suppressedExceptions = null;
            }
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}

我们先假设不存在aop 与多线程竞争的情况,此时我们只需要一级缓存就可以解决循环依赖的问题

即不存在aop且不考虑多线程的情况下,只要一个map1 我们就能解决循环依赖的问题

3.现在我们考虑aop

假设A 有属性B ,B 有属性A ,且我们对A 做了aop,在创建B 的时候我们希望拿到的是A 的aop 代理对象,此时假设填充B 的时候获取A ,

假设如果我们用一层缓存解决?

可以对是否aop 代理过存储一个标志位,此时必然在后面获取bean 的时候都要多一层判断,所以此时需要二层缓存来解决

一层存放aop 代理过的对象或不需要aop 的对象,一层存放非aop 对象,即创建工厂。

4.我们再考虑并发的情况,即多线程获取一个单例

这里如果熟悉单例模式的创建过程,就不难理解第二个方法中的synchronized 与加锁后再次尝试从一级缓存中获取,把第一个方法可以看作第一次非空判断,那么就是双重锁检查保证单例。但是只做了双重锁检查保证单例可以支持多线程操作吗?

答案是否定的,假设我们为了解决aop 使用了二层缓存,对于A 来说 并没有完全完成赋值和初始化的操作,但是由于A 依赖B ,B依赖于A的aop 代理对象,此时A 已经在一级中了,那么线程2边能从map1 中获取到 对象A,(当然你也可以对整个创建过程加锁,那么就不存在多线程问题)。但是此时A 对象并没有完成赋值与init 的方法执行,这样很容易造成线程2 的异常,所以还需要一层缓存将完成整个创建过程的bean ,与没有创建完成的纯洁bean 隔离开,所以才有了3级缓存。

此时再考虑3级缓存的获取,基于并发的考虑就不难理解第一个方法为何要synchronized 了,即保证并发下,缓存只在一层缓存中存在。

综上来说,我的理解,aop 问题需要两层缓存来解决,而考虑多线程并发的情况下,所以需要三层缓存来解决。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文