欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

聊聊Spring循环依赖三级缓存是否可以减少为二级缓存的情况

程序员文章站 2022-09-25 23:44:38
基于spring-5.1.5.release问题都知道spring通过三级缓存来解决循环依赖的问题。但是是不是必须三级缓存才能解决,二级缓存不能解决吗?要分析是不是可以去掉其中一级缓存,就先过一遍sp...

基于spring-5.1.5.release

问题

都知道spring通过三级缓存来解决循环依赖的问题。但是是不是必须三级缓存才能解决,二级缓存不能解决吗?

要分析是不是可以去掉其中一级缓存,就先过一遍spring是如何通过三级缓存来解决循环依赖的。

循环依赖

所谓的循环依赖,就是两个或则两个以上的bean互相依赖对方,最终形成闭环。比如“a对象依赖b对象,而b对象也依赖a对象”,或者“a对象依赖b对象,b对象依赖c对象,c对象依赖a对象”;类似以下代码:

public class a {
 private b b;
}
 
public class b {
 private a a;
}

常规情况下,会出现以下情况:

通过构建函数创建a对象(a对象是半成品,还没注入属性和调用init方法)。

a对象需要注入b对象,发现对象池(缓存)里还没有b对象(对象在创建并且注入属性和初始化完成之后,会放入对象缓存里)。

通过构建函数创建b对象(b对象是半成品,还没注入属性和调用init方法)。

b对象需要注入a对象,发现对象池里还没有a对象。

创建a对象,循环以上步骤。

三级缓存

spring解决循环依赖的核心思想在于提前曝光:

通过构建函数创建a对象(a对象是半成品,还没注入属性和调用init方法)。

a对象需要注入b对象,发现缓存里还没有b对象,将半成品对象a放入半成品缓存。

通过构建函数创建b对象(b对象是半成品,还没注入属性和调用init方法)。

b对象需要注入a对象,从半成品缓存里取到半成品对象a。

b对象继续注入其他属性和初始化,之后将完成品b对象放入完成品缓存。

a对象继续注入属性,从完成品缓存中取到完成品b对象并注入。

a对象继续注入其他属性和初始化,之后将完成品a对象放入完成品缓存。

其中缓存有三级:

/** 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);
缓存 说明
singletonobjects 第一级缓存,存放可用的成品bean。
earlysingletonobjects 第二级缓存,存放半成品的bean,半成品的bean是已创建对象,但是未注入属性和初始化。用以解决循环依赖。
singletonfactories 第三级缓存,存的是bean工厂对象,用来生成半成品的bean并放入到二级缓存中。用以解决循环依赖。

要了解原理,最好的方法就是阅读源码,从创建bean的方法abstractautowirecapablebeanfactor.docreatebean入手。

1. 在构造bean对象之后,将对象提前曝光到缓存中,这时候曝光的对象仅仅是构造完成,还没注入属性和初始化。

public abstract class abstractautowirecapablebeanfactory extends abstractbeanfactory
  implements autowirecapablebeanfactory {
 protected object docreatebean(final string beanname, final rootbeandefinition mbd, final @nullable object[] args)
   throws beancreationexception {   
  ……
  // 是否提前曝光
  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. 提前曝光的对象被放入map<string, objectfactory<?>> singletonfactories缓存中,这里并不是直接将bean放入缓存,而是包装成objectfactory对象再放入。

public class defaultsingletonbeanregistry extends simplealiasregistry implements singletonbeanregistry {
 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);
   }
  }
 }
}
public interface objectfactory<t> {
 t getobject() throws beansexception;
} 

3. 为什么要包装一层objectfactory对象?

如果创建的bean有对应的代理,那其他对象注入时,注入的应该是对应的代理对象;但是spring无法提前知道这个对象是不是有循环依赖的情况,而正常情况下(没有循环依赖情况),spring都是在创建好完成品bean之后才创建对应的代理。这时候spring有两个选择:

不管有没有循环依赖,都提前创建好代理对象,并将代理对象放入缓存,出现循环依赖时,其他对象直接就可以取到代理对象并注入。

不提前创建好代理对象,在出现循环依赖被其他对象注入时,才实时生成代理对象。这样在没有循环依赖的情况下,bean就可以按着spring设计原则的步骤来创建。

spring选择了第二种方式,那怎么做到提前曝光对象而又不生成代理呢?

spring就是在对象外面包一层objectfactory,提前曝光的是objectfactory对象,在被注入时才在objectfactory.getobject方式内实时生成代理对象,并将生成好的代理对象放入到第二级缓存map<string, object> earlysingletonobjects。

addsingletonfactory(beanname, () -> getearlybeanreference(beanname, mbd, bean));:

public abstract class abstractautowirecapablebeanfactory extends abstractbeanfactory
  implements autowirecapablebeanfactory {
 
 protected object getearlybeanreference(string beanname, rootbeandefinition mbd, object bean) {
  object exposedobject = bean;
  if (!mbd.issynthetic() && hasinstantiationawarebeanpostprocessors()) {
   for (beanpostprocessor bp : getbeanpostprocessors()) {
    if (bp instanceof smartinstantiationawarebeanpostprocessor) {
     smartinstantiationawarebeanpostprocessor ibp = (smartinstantiationawarebeanpostprocessor) bp;
     exposedobject = ibp.getearlybeanreference(exposedobject, beanname);
    }
   }
  }
  return exposedobject;
 }
}

为了防止对象在后面的初始化(init)时重复代理,在创建代理时,earlyproxyreferences缓存会记录已代理的对象。

public abstract class abstractautoproxycreator extends proxyprocessorsupport
  implements smartinstantiationawarebeanpostprocessor, beanfactoryaware {
 private final map<object, object> earlyproxyreferences = new concurrenthashmap<>(16);
   
 @override
 public object getearlybeanreference(object bean, string beanname) {
  object cachekey = getcachekey(bean.getclass(), beanname);
  this.earlyproxyreferences.put(cachekey, bean);
  return wrapifnecessary(bean, beanname, cachekey);
 }  
}  

4. 注入属性和初始化

提前曝光之后:

通过populatebean方法注入属性,在注入其他bean对象时,会先去缓存里取,如果缓存没有,就创建该对象并注入。

通过initializebean方法初始化对象,包含创建代理。

public abstract class abstractautowirecapablebeanfactory extends abstractbeanfactory
  implements autowirecapablebeanfactory {
 protected object docreatebean(final string beanname, final rootbeandefinition mbd, final @nullable object[] args)
   throws beancreationexception {
  ……
  // initialize the bean instance.
  object exposedobject = bean;
  try {
   populatebean(beanname, mbd, instancewrapper);
   exposedobject = initializebean(beanname, exposedobject, mbd);
  }
  catch (throwable ex) {
   if (ex instanceof beancreationexception && beanname.equals(((beancreationexception) ex).getbeanname())) {
    throw (beancreationexception) ex;
   }
   else {
    throw new beancreationexception(
      mbd.getresourcedescription(), beanname, "initialization of bean failed", ex);
   }
  }
  ……
 }  
} 
// 获取要注入的对象
public class defaultsingletonbeanregistry extends simplealiasregistry implements singletonbeanregistry {
 protected object getsingleton(string beanname, boolean allowearlyreference) {
  // 一级缓存
  object singletonobject = this.singletonobjects.get(beanname);
  if (singletonobject == null && issingletoncurrentlyincreation(beanname)) {
   synchronized (this.singletonobjects) {
    // 二级缓存
    singletonobject = this.earlysingletonobjects.get(beanname);
    if (singletonobject == null && allowearlyreference) {
     // 三级缓存
     objectfactory<?> singletonfactory = this.singletonfactories.get(beanname);
     if (singletonfactory != null) {
      singletonobject = singletonfactory.getobject();
      this.earlysingletonobjects.put(beanname, singletonobject);
      this.singletonfactories.remove(beanname);
     }
    }
   }
  }
  return singletonobject;
 }
} 

5. 放入已完成创建的单例缓存

在经历了以下步骤之后,最终通过addsingleton方法将最终生成的可用的bean放入到单例缓存里。

abstractbeanfactory.dogetbean ->
defaultsingletonbeanregistry.getsingleton ->
abstractautowirecapablebeanfactory.createbean ->
abstractautowirecapablebeanfactory.docreatebean ->
defaultsingletonbeanregistry.addsingleton
public class defaultsingletonbeanregistry extends simplealiasregistry implements singletonbeanregistry {
 
 /** cache of singleton objects: bean name to bean instance. */
 private final map<string, object> singletonobjects = new concurrenthashmap<>(256);
 
 /** cache of singleton factories: bean name to objectfactory. */
 private final map<string, objectfactory<?>> singletonfactories = new hashmap<>(16);
 
 /** cache of early singleton objects: bean name to bean instance. */
 private final map<string, object> earlysingletonobjects = new hashmap<>(16);
 
 protected void addsingleton(string beanname, object singletonobject) {
  synchronized (this.singletonobjects) {
   this.singletonobjects.put(beanname, singletonobject);
   this.singletonfactories.remove(beanname);
   this.earlysingletonobjects.remove(beanname);
   this.registeredsingletons.add(beanname);
  }
 }
} 

二级缓存

上面第三步《为什么要包装一层objectfactory对象?》里讲到有两种选择:

不管有没有循环依赖,都提前创建好代理对象,并将代理对象放入缓存,出现循环依赖时,其他对象直接就可以取到代理对象并注入。

不提前创建好代理对象,在出现循环依赖被其他对象注入时,才实时生成代理对象。这样在没有循环依赖的情况下,bean就可以按着spring设计原则的步骤来创建。

sping选择了第二种,如果是第一种,就会有以下不同的处理逻辑:

在提前曝光半成品时,直接执行getearlybeanreference创建到代理,并放入到缓存earlysingletonobjects中。

有了上一步,那就不需要通过objectfactory来延迟执行getearlybeanreference,也就不需要singletonfactories这一级缓存。

这种处理方式可行吗?

聊聊Spring循环依赖三级缓存是否可以减少为二级缓存的情况

这里做个试验,对abstractautowirecapablebeanfactory做个小改造,在放入三级缓存之后立刻取出并放入二级缓存,这样三级缓存的作用就完全被忽略掉,就相当于只有二级缓存。

public abstract class abstractautowirecapablebeanfactory extends abstractbeanfactory
  implements autowirecapablebeanfactory {
 protected object docreatebean(final string beanname, final rootbeandefinition mbd, final @nullable object[] args)
   throws beancreationexception {   
  ……
  // 是否提前曝光
  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));
   // 立刻从三级缓存取出放入二级缓存
   getsingleton(beanname, true);
  }
  ……
 } 
}  

测试结果是可以的,并且从源码上分析可以得出两种方式性能是一样的,并不会影响到sping启动速度。那为什么sping不选择二级缓存方式,而是要额外加一层缓存?

如果要使用二级缓存解决循环依赖,意味着bean在构造完后就创建代理对象,这样违背了spring设计原则。

spring结合aop跟bean的生命周期,是在bean创建完全之后通过annotationawareaspectjautoproxycreator这个后置处理器来完成的,在这个后置处理的postprocessafterinitialization方法中对初始化后的bean完成aop代理。

如果出现了循环依赖,那没有办法,只有给bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。