当前位置: 移动技术网 > IT编程>开发语言>Java > spring循环依赖策略解析

spring循环依赖策略解析

2019年07月19日  | 移动技术网IT编程  | 我要评论

循环依赖

所谓循环依赖就是多个bean之间依赖关系形成一个闭环,例如a->b->c->...->a 这种情况,当然,最简单的循环依赖就是2个bean之间互相依赖:a->b(a依赖b), b->a(b依赖a) 。在spring中,如果a->b,那么在创建a的过程中会去创建b,在创建b(或b的依赖)的过程中又发现b->a,这个时候就出现了循环依赖的现象。

循环依赖的解决

spring中的循环依赖只有当

1.bean是单例,
2.通过属性注入的情况

这两个条件满足的情况下是没问题的。但是如果是通过构造器依赖,或者不是单例模式的情况下循环依赖就会抛出异常beancurrentlyincreationexception。下面从代码层面上解析一下为什么。

prototype的循环依赖问题

为什么最先介绍prototype的循环依赖呢,因为可以顺便介绍在spring中创建bean的流程核心流程:在abstractfoctory的dogetbean的方法。这个方法很长,这里只写出核心逻辑,并在注解上注明了个人理解:

protected <t> t dogetbean(
    final string name, final class<t> requiredtype, final object[] args, boolean typecheckonly)
    throws beansexception {
  
  final string beanname = transformedbeanname(name);
  object bean;
  
  //尝试获取单例对象,因为spring大部分的bean都是单例的,所以这里先尝试能否获取。
  registered singletons.
  object sharedinstance = getsingleton(beanname);
  //单例存在的情况下,那么beanname返回的肯定是单例类,但是这里还需要判断是不是factorybean
  if (sharedinstance != null && args == null) {
    ...
    //factorybean应该返回getobject()对象
    bean = getobjectforbeaninstance(sharedinstance, name, beanname, null);
  }
  
  else {
    //走到这里,有可能beanname是单例模式,但之前并没有实例化,或者是prototype类型。
    //首先判断不是循环依赖,这里的循环依赖指的是prototype类型
    if (isprototypecurrentlyincreation(beanname)) {
      throw new beancurrentlyincreationexception(beanname);
    }
  
  
    try {
      final rootbeandefinition mbd = getmergedlocalbeandefinition(beanname);
      // 如果是单例,则创建单例模式
      if (mbd.issingleton()) {
        // !!!这里是解决单例循环依赖的关键,后面再分析
        sharedinstance = getsingleton(beanname, new objectfactory<object>() {
          @override
          public object getobject() throws beansexception {
            try {
              return createbean(beanname, mbd, args);
            }
            catch (beansexception ex) {
              throw ex;
            }
          }
        });
        bean = getobjectforbeaninstance(sharedinstance, name, beanname, mbd);
      }
  
      else if (mbd.isprototype()) {
        // 原型模式,则创建一个新对象.
        object prototypeinstance = null;
        try {
          /*这里是prototype循环依赖的问题,会记录在map中beanname,
          如果在解决当前bean的依赖过程中还依赖当前bean,
          则说明了出现了循环依赖
          */
              beforeprototypecreation(beanname);
              prototypeinstance = createbean(beanname, mbd, args);
        }
        finally {
          //对应beforeprototypecreation(),从map中移除
              afterprototypecreation(beanname);
        }
        bean = getobjectforbeaninstance(prototypeinstance, name, beanname, mbd);
      }
      ...
    }
  }
  
  ...
  return (t) bean;
}

可以看出,该流程中就考虑了prototype的循环依赖的问题,只要在创建prototype的bean中出现循环依赖那么就抛出异常。但是在singleton的情况下,则通过另外的方式来解决。

singleton的循环依赖之构造注入

在上面介绍中,出现了一个很关键的地方:

sharedinstance = getsingleton(beanname, new objectfactory<object>() {
  @override
  public object getobject() throws beansexception {
    try {
      return createbean(beanname, mbd, args);
    }
    catch (beansexception ex) {
      throw ex;
    }
  }
});

这个getsingleton涉及到了objectfactory这个接口类,这个接口的功能和factorybean类似,但是主要是用来解决循环依赖的。在初始化过程同决定返回的singleton对象是。关于单例的对象的创建,又要介绍一下defaultsingletonbeanregistry这个类,这个类主要用来帮助创建单例模式,其中主要的属性:

/** 缓存创建的单例对象: bean名字 --> bean对象 */
private final map<string, object> singletonobjects = new concurrenthashmap<string, object>(256);

/** 缓存单例的factory,就是objectfactory这个东西,: bean name --> objectfactory */
private final map<string, objectfactory<?>> singletonfactories = new hashmap<string, objectfactory<?>>(16);

/** 也是缓存创建的单例对象,功能和singletonobjects不一样,
在bean构造成功之后,属性初始化之前会把对象放入到这里,
主要是用于解决属性注入的循环引用: bean name --> bean instance 
*/
private final map<string, object> earlysingletonobjects = new hashmap<string, object>(16);

/** 记录在创建单例对象中循环依赖的问题,还记得prototype中又记录创建过程中依赖的map吗?
在prototype中只要出现了循环依赖就抛出异常,而在单例中会尝试解决 */
private final set<string> singletonscurrentlyincreation =
    collections.newsetfrommap(new concurrenthashmap<string, boolean>(16));

现在回过来看getsingleton(beanname, new objectfactory<object>()这个方法的实现。

public object getsingleton(string beanname, objectfactory<?> singletonfactory) {

  synchronized (this.singletonobjects) {
    //尝试在singletonobjects中获取
    object singletonobject = this.singletonobjects.get(beanname);
    if (singletonobject == null) {
      //不存在则创建
      //把当前beanname加入到singletonscurrentlyincreation中
      beforesingletoncreation(beanname);
      try {
        
        singletonobject = singletonfactory.getobject();
      }
      ...
      finally {
        ...
        //从singletonscurrentlyincreation中删除beanname
        aftersingletoncreation(beanname);
      }
    }
    return (singletonobject != null_object ? singletonobject : null);
  }
}

这段逻辑是不是和prototype中解决循环类似,这里其实就是调用了objectfactory的getobject()获取对象,回过头去看前面代码,objectfactory的getobject()方法实际调用的是createbean(beanname, mbd, args)。说到createbean(beanname, mbd, args)又不得不说abstractautowirecapablebeanfactory这个类,主要功能就是完成依赖注入的bean的创建,这个类的createbean方法代码如下,注意注解说明:

@override
protected object createbean(string beanname, rootbeandefinition mbd, object[] args) throws beancreationexception {
  ...
  object beaninstance = docreatebean(beanname, mbdtouse, args);
  ...
}

protected object docreatebean(final string beanname, final rootbeandefinition mbd, final object[] args)
      throws beancreationexception {

  // 实例化bean
  beanwrapper instancewrapper = null;
  if (mbd.issingleton()) {
    instancewrapper = this.factorybeaninstancecache.remove(beanname);
  }
  if (instancewrapper == null) {
    //如果没实例化则创建新的beanwrapper
    //如果是通过构造器注入,这里是一个关键点
    /*
    因为在a初始化的时候发现构造函数依赖b,就会去实例化b,
    然后b也会运行到这段逻辑,构造函数中发现依赖a,
    这个时候就会抛出循环依赖的异常
    */
        instancewrapper = createbeaninstance(beanname, mbd, args);
  }
  

  //如果当前是单例,并且allowcircularreferences为true(默认就是true,除非我们不希望spring帮我们解决)
  boolean earlysingletonexposure = (mbd.issingleton() && this.allowcircularreferences &&
      issingletoncurrentlyincreation(beanname));
  if (earlysingletonexposure) {
    /*
    !!!这里很重要,把构造成功,但属性还没注入的
    的bean加到singletonfactory中,这样再解决a的依赖
    过程中如果依赖a,就把这个半成品返回回去。
    */
    addsingletonfactory(beanname, new objectfactory<object>() {
      @override
      public object getobject() throws beansexception {
        return getearlybeanreference(beanname, mbd, bean);
      }
    });
  }

  
  object exposedobject = bean;
  try {
    //自动注入属性
    populatebean(beanname, mbd, instancewrapper);
    if (exposedobject != null) {
      exposedobject = initializebean(beanname, exposedobject, mbd);
    }
  }
  ...

  return exposedobject;
}

注解已经注明了我的理解。就不再赘述

总结

上面代码是我一边debug一个写下的,现在写完了,根据自己的理解总结一下。

相关类说明

abstractbeanfactory,这个类中包含了bean创建的主要流程,在dogetbean这个方法中包含了对prototype循环依赖处理。逻辑很简单,出现了循环依赖则直接抛出异常

defaultsingletonbeanregister 用于管理singleton的对象的创建,以及解决循环依赖的问题,其中解决循环依赖的关键属性就是了earlysingletonobjects,他会在构造singleton对象过程中暂时缓存构造成功,但属性还未注入的对象,这样就可以解决循环依赖的问题。

abstractautowirecapablebeanfactory,自动注入的相关逻辑,包自动注入的对象的创建、初始化和注入。但如果在调用构造函数中发现了循环依赖,则抛出异常

objectfactory,这个接口功能和factorybean类似,但是为了解决循环依赖,他决定了在获取的getsingleton()是一个完成品还是一个半成品。

思考

如果a--构造依赖->b,b--属性依赖-->a,例如:

@component
public class beana {
  private beanb beanb;

  @autowired
  public beana(beanb beanb) {
    this.beanb = beanb;
  }
}

@component
public class beanb {
  @autowired
  private beana beana;
}

这种情况会异常吗?提示:都有可能

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网