zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Spring依赖注入(五):盘一盘Spring的三级缓存

2023-09-14 09:14:06 时间

前言

前面通过四篇文章基本上把Spring bean的依赖注入和循环依赖问题的解决盘清楚了,但是盘完回头一看,有一件事我没有说太清楚,那就是Spring的依赖注入和循环依赖的解决,使用到了Spring的一、二、三级缓存,那么问题来了,Spring的一、二、三级缓存分别是什么?Spring的一、二、三级缓存在bean的创建过程中是如何发挥作用的?自己挖的坑,说什么也得自己给补上,于是乎那就再开辟一篇,盘一盘Spring的三级缓存吧。

Spring依赖注入(一):字段注入的方式是如何工作的?

Sprng依赖注入(二):setter注入是如何工作的?

Sprng依赖注入(三):构造方法注入是如何工作的?

Spring依赖注入(四):Bean的循环依赖是如何产生和解决的?

什么是Spring的三级缓存?

1、singletonObjects:Spring的第一级缓存,用于存储完成实例化、属性注入、初始化的单例bean

2、earlySingletonObjects:Spring的第二级缓存,用于存储完成实例化,未做属性注入和初始化的的单例bean

3、singletonFactories:Spring的第三缓存,存储的是完成实例化、未完成属性注入、初始化的bean的工厂,实际就是那个用lambda表达式写的函数式接口实现,() -> getEarlyBeanReference(beanName, mbd, bean),可以提前获取到未完成创建的bean;

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

   /**Spring的第一级缓存,用于存储完成实例化、属性注入、初始化的单例bean */
   private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

   /** Spring的第三缓存,存储的是完成实例化、未完成属性注入、初始化的bean的工厂,
       实际就是那个用lambda表达式写的函数式接口实现,可以获取到bean
    */
   private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

   /** Spring的第二级缓存,用于存储完成实例化,未做属性注入和初始化的的单例bean */
   private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

   /** 完成注册,即实例化、属性注入、初始化的单例bean都在这里 */
   private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

   /** 正在创建中的单例bean的beanName在这里面 */
   private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

}

Spring的三级缓存是如何工作的?

Spring的一级缓存是如何工作的?

1、容器启动、非懒加载的单例bean注册入口:AbstractApplicationContext#refresh-->AbstractApplicationContext#finishBeanFactoryInitialization-->DefaultListableBeanFactory#preInstantiateSingletons-->AbstractBeanFactory#getBean(java.lang.String)-->AbstractBeanFactory#doGetBean

2、AbstractBeanFactory#doGetBean主要逻辑是,判断新创建的bean是否存在于Spring的一缓存中,如果在Spring一级缓存中可以获取到bean,则经过一些简单的其他逻辑过程就可以返回了;如果在Spring一级缓存中可以获取到不到bean,则调用DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory)开始创建bean;

3、DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory),参数1是beanName,参数2的类型是ObjectFactory,实际是一个lambda表达式() -> {return createBean(beanName, mbd, args);},ObjectFactory内有一个抽象方法getObject(),singletonFactory.getObject()触发执行时,触发lambda表达式中createBean(beanName, mbd, args)执行,lambda表达式的返回值即singletonFactory.getObject()返回值,lambda的表达式的逻辑就是创建一个合格的bean,即实例化、属性注入、初始化完成,可以正式使用的bean;

4、得到正式的bean后,则调用DefaultSingletonBeanRegistry#addSingleton,把bean设置到Spring一级缓存中去,并移除二级缓存、三级缓存中的内容;如果其他bean依赖当前bean,则可以直接从Spring的一级缓存中得到;

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "Bean name must not be null");
   synchronized (this.singletonObjects) {
       //先从Spring的一级缓存中,尝试获取一下bean
      Object singletonObject = this.singletonObjects.get(beanName);
      //如果从一级缓存中获取bean为null,则开始创建bean
      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 + "'");
         }
         beforeSingletonCreation(beanName);
         //是否属于新创建单例bean的标志位,初始值是false
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
         }
         try {
             //开始调用AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])创建一个合格的bean
            singletonObject = singletonFactory.getObject();
            //新创建的单例bean创建成功后,设置标志位为true
            newSingleton = true;
         }
         catch (IllegalStateException ex) {
            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);
         }
         //新创建的单例bean标志位为true时,把bean设置到Spring的一级缓存中去;
         if (newSingleton) {
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}
protected void addSingleton(String beanName, Object singletonObject) {
    //操作Spring的一级缓存时,先用synchronized关键字锁定要操作的缓存资源
   synchronized (this.singletonObjects) {
       //把合格的bean设置到Spring一级缓存中,缓存key为beanName,value为bean本身
      this.singletonObjects.put(beanName, singletonObject);
      //一级缓存设置完后,移除三级缓存、二级缓存以及注册完成单例bean的缓存中的内容
      this.singletonFactories.remove(beanName);
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
   }
}

Spring的二、三级缓存是如何工作的?

1、Spring中bean的创建主要逻辑在AbstractAutowireCapableBeanFactory#doCreateBean中,其中createBeanInstance(beanName, mbd, args)完成bean实例化,populateBean(beanName, mbd, instanceWrapper)完成bean的属性注入,在bean实例化完成后,开始属性注入前,有这么一段:DefaultSingletonBeanRegistry#addSingletonFactory被调用,即把实例化完成的bean设置到Spring的三级缓存中去,缓存key为beanName,而缓存value实际并不是一个具体的bean对象,value的类型是ObjectFactory,实际传入的是一个lambda表达式实现() -> getEarlyBeanReference(beanName, mbd, bean),ObjectFactory接口里有一个抽象方法getObject(),ObjectFactory#getObject()方法被触发的时候,会执行lambda表达式的内容; 而lambda表达式里的getEarlyBeanReference()作用就是获取提前暴露的半成品bean的引用;

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   if (logger.isTraceEnabled()) {
      logger.trace("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
   }
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

2、DefaultSingletonBeanRegistry#addSingletonFactory内的逻辑比较简单,即把不在Spring一级缓存中的“半成品”bean,放到Spring的三级缓存(singletonFactories)中;

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);
      }
   }
}

3、如果bean之间发生了循环依赖,在获取依赖bean的时候,就会尝试先从Spring的三级缓存中去获取提前暴露的“半成品”bean,以便结束循环依赖bean之间的互相等待,主要的逻辑在DefaultSingletonBeanRegistry#getSingleton(String, boolean),参数1是beanName,参数2的值为true,表示允许获取到提前暴露的“半成品”bean引用;DefaultSingletonBeanRegistry#getSingleton(String, boolean)中,调用getObject(),触发了AbstractAutowireCapableBeanFactory#getEarlyBeanReference()的调用,这里又涉及到了Spring一个特殊扩展点Springboot扩展点之SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference(),这个方法主要作用就是Spring发生循环依赖时,可以获取到提前暴露的“半成品”bean引用,而Spring内部关于SmartInstantiationAwareBeanPostProcessor有一个重要实现,即AbstractAutoProxyCreator,是AspectJAwareAdvisorAutoProxyCreator的父类,如果bean使用了aop而产生了一个代理对象,那么getObject()返回值就是bean的代理对象,并不是实际bean,而移入到二级缓存中的是“半成品”的bean的代理对象;如果没有使用到aop,那么这里就是一个普通的”半成品“bean引用,这里就是Spring二级缓存、三级缓存的妙处;

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   // 快速的检查一一下bean是否存在于Spring一级缓存、二级缓存
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      singletonObject = this.earlySingletonObjects.get(beanName);
      if (singletonObject == null && allowEarlyReference) {
          //如果bean不存在于Spring的一级缓存、二级缓存,则锁定一级缓存
         synchronized (this.singletonObjects) {
            //锁定一级缓存后,再尝试获取一下
            singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                //尝试从一级缓存中获取的结果为null,尝试从二级缓存中获取
               singletonObject = this.earlySingletonObjects.get(beanName);
               if (singletonObject == null) {
                   //尝试从二级缓存中获取结果为null,则尝试从三级缓存中获取
                  ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                  if (singletonFactory != null) {
                      //从三级缓存中获取结果不为空,但是三级缓存中并不是实际bean,而是一个bean工厂类
                      //调用getObject()触发AbstractAutowireCapableBeanFactory#getEarlyBeanReference()
                     singletonObject = singletonFactory.getObject();
                     //getEarlyBeanReference()获取提前暴露的“半成品”bean后,移入到Spring的二级缓存,并从三级缓存中移除
                     this.earlySingletonObjects.put(beanName, singletonObject);
                     this.singletonFactories.remove(beanName);
                  }
               }
            }
         }
      }
   }
   return singletonObject;
}
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
   Object cacheKey = getCacheKey(bean.getClass(), beanName);
   this.earlyProxyReferences.put(cacheKey, bean);
   //如果bean使用了aop,则产生bean代理对象,否则还是bean本身
   return wrapIfNecessary(bean, beanName, cacheKey);
}

总结

Spring依赖注入(一):字段注入的方式是如何工作的?中示例类Student和Teacher为例,即:

1、实例化student前,判断新创建的student是否存在于Spring的一缓存中,如果在Spring一级缓存中可以获取到student,则经过一些简单的其他逻辑过程就可以返回了;如果在Spring一级缓存中可以获取到不到student,则调用DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory)开始创建student;

2、student实例化完成后,把“半成品”的student的工厂类放入Spring三级缓存中;

3、接着开始student依赖属性teacher的注入,这时发现依赖属性teacher未实例化,于是开始teacher的实例化;

4、teacher实例化前也会判断创建的teacher是否存在于Spring的一缓存中,不存于一级缓存中才开始teacher实例化;teacher实例化后,也会把“半成品”的teacher的工厂类放入Spring三级缓存中;

5、然后接着teacher对象属性注入、初始化,但是Teacher的实例化完后,开始属性注入student对象的时候,student对象还没有创建完成呢,但是Spring有三级缓存里有student的工厂类;

6、再接着getEarlyBeanReference()被触发,如果student使用了aop,那么getEarlyBeanReference()返回值就是student的代理对象,移入到二级缓存中的是“半成品”的student的代理对象;如果没有使用到aop,那么这里移入到二级缓存中的就是一个普通的”半成品“student引用;

7、teacher利用”半成品“的student完成属性注入、初始化等bean创建操作后,teacher被添加到Spring的一级缓存中,student就可以使用创建好的teacher完成依赖属性注入、初始化后,student从Spring的二级缓存中移到一级缓存中,完成student的创建,至此发生循环依赖的student、teacher互相成为了一个完成的bean。