服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - 详解Spring循环依赖的解决方案

详解Spring循环依赖的解决方案

2021-05-05 10:48数齐 Java教程

这篇文章主要介绍了详解Spring循环依赖的解决方案,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

spring针对bean之间的循环依赖,有自己的处理方案。关键点就是三级缓存。当然这种方案不能解决所有的问题,他只能解决bean单例模式下非构造函数的循环依赖。

我们就从a->b->c-a这个初始化顺序,也就是a的bean中需要b的实例,b的bean中需要c的实例,c的bean中需要a的实例,当然这种需要不是构造函数那种依赖。前提条件有了,我们就可以开始了。毫无疑问,我们会先初始化a.初始化的方法是org.springframework.beans.factory.support.abstractbeanfactory#dogetbean

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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;
 
  // eagerly check singleton cache for manually registered singletons.
  object sharedinstance = getsingleton(beanname); //关注点1
  if (sharedinstance != null && args == null) {
   if (logger.isdebugenabled()) {
    if (issingletoncurrentlyincreation(beanname)) {
     logger.debug("returning eagerly cached instance of singleton bean '" + beanname +
       "' that is not fully initialized yet - a consequence of a circular reference");
    }
    else {
     logger.debug("returning cached instance of singleton bean '" + beanname + "'");
    }
   }
   bean = getobjectforbeaninstance(sharedinstance, name, beanname, null);
  }
 
  else {
   // fail if we're already creating this bean instance:
   // we're assumably within a circular reference.
   if (isprototypecurrentlyincreation(beanname)) {
    throw new beancurrentlyincreationexception(beanname);
   }
 
   // check if bean definition exists in this factory.
   beanfactory parentbeanfactory = getparentbeanfactory();
   if (parentbeanfactory != null && !containsbeandefinition(beanname)) {
    // not found -> check parent.
    string nametolookup = originalbeanname(name);
    if (args != null) {
     // delegation to parent with explicit args.
     return (t) parentbeanfactory.getbean(nametolookup, args);
    }
    else {
     // no args -> delegate to standard getbean method.
     return parentbeanfactory.getbean(nametolookup, requiredtype);
    }
   }
 
   if (!typecheckonly) {
    markbeanascreated(beanname);
   }
 
   try {
    final rootbeandefinition mbd = getmergedlocalbeandefinition(beanname);
    checkmergedbeandefinition(mbd, beanname, args);
 
    // guarantee initialization of beans that the current bean depends on.
    string[] dependson = mbd.getdependson();
    if (dependson != null) {
     for (string dependsonbean : dependson) {
      if (isdependent(beanname, dependsonbean)) {
       throw new beancreationexception(mbd.getresourcedescription(), beanname,
         "circular depends-on relationship between '" + beanname + "' and '" + dependsonbean + "'");
      }
      registerdependentbean(dependsonbean, beanname);
      getbean(dependsonbean);
     }
    }
 
    // create bean instance.
    if (mbd.issingleton()) {
     //关注点2
     sharedinstance = getsingleton(beanname, new objectfactory<object>() {
      @override
      public object getobject() throws beansexception {
       try {
        return createbean(beanname, mbd, args);
       }
       catch (beansexception ex) {
        // explicitly remove instance from singleton cache: it might have been put there
        // eagerly by the creation process, to allow for circular reference resolution.
        // also remove any beans that received a temporary reference to the bean.
        destroysingleton(beanname);
        throw ex;
       }
      }
     });
     bean = getobjectforbeaninstance(sharedinstance, name, beanname, mbd);
    }
 
    else if (mbd.isprototype()) {
     // it's a prototype -> create a new instance.
     object prototypeinstance = null;
     try {
      beforeprototypecreation(beanname);
      prototypeinstance = createbean(beanname, mbd, args);
     }
     finally {
      afterprototypecreation(beanname);
     }
     bean = getobjectforbeaninstance(prototypeinstance, name, beanname, mbd);
    }
 
    else {
     string scopename = mbd.getscope();
     final scope scope = this.scopes.get(scopename);
     if (scope == null) {
      throw new illegalstateexception("no scope registered for scope name '" + scopename + "'");
     }
     try {
      object scopedinstance = scope.get(beanname, new objectfactory<object>() {
       @override
       public object getobject() throws beansexception {
        beforeprototypecreation(beanname);
        try {
         return createbean(beanname, mbd, args);
        }
        finally {
         afterprototypecreation(beanname);
        }
       }
      });
      bean = getobjectforbeaninstance(scopedinstance, name, beanname, mbd);
     }
     catch (illegalstateexception ex) {
      throw new beancreationexception(beanname,
        "scope '" + scopename + "' is not active for the current thread; consider " +
        "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
        ex);
     }
    }
   }
   catch (beansexception ex) {
    cleanupafterbeancreationfailure(beanname);
    throw ex;
   }
  }
 
  // check if required type matches the type of the actual bean instance.
  if (requiredtype != null && bean != null && !requiredtype.isassignablefrom(bean.getclass())) {
   try {
    return gettypeconverter().convertifnecessary(bean, requiredtype);
   }
   catch (typemismatchexception ex) {
    if (logger.isdebugenabled()) {
     logger.debug("failed to convert bean '" + name + "' to required type [" +
       classutils.getqualifiedname(requiredtype) + "]", ex);
    }
    throw new beannotofrequiredtypeexception(name, requiredtype, bean.getclass());
   }
  }
  return (t) bean;
 }

这个方法很长我们一点点说。先看我们的关注点1 object sharedinstance = getsingleton(beanname)根据名称从单例的集合中获取单例对象,我们看下这个方法,他最终是org.springframework.beans.factory.support.defaultsingletonbeanregistry#getsingleton(java.lang.string, boolean)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 != null_object ? singletonobject : null);
}

大家一定要注意这个方法,很关键,我们开篇提到了三级缓存,使用点之一就是这里。到底是哪三级缓存呢,第一级缓存singletonobjects里面放置的是实例化好的单例对象。第二级earlysingletonobjects里面存放的是提前曝光的单例对象(没有完全装配好)。第三级singletonfactories里面存放的是要被实例化的对象的对象工厂。解释好了三级缓存,我们再看看逻辑。第一次进来this.singletonobjects.get(beanname)返回的肯定是null。然后issingletoncurrentlyincreation决定了能否进入二级缓存中获取数据。

?
1
2
3
public boolean issingletoncurrentlyincreation(string beanname) {
  return this.singletonscurrentlyincreation.contains(beanname);
 }

singletonscurrentlyincreation这个set中有没有包含传入的beanname,前面没有地方设置,所以肯定不包含,所以这个方法返回false,后面的流程就不走了。getsingleton这个方法返回的是null。

下面我们看下关注点2.也是一个getsingleton只不过他是真实的创建bean的过程,我们可以看到传入了一个匿名的objectfactory的对象,他的getobject方法中调用的是createbean这个真正的创建bean的方法。当然我们可以先搁置一下,继续看我们的getsingleton方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public object getsingleton(string beanname, objectfactory<?> singletonfactory) {
  assert.notnull(beanname, "'beanname' must not be null");
  synchronized (this.singletonobjects) {
   object singletonobject = this.singletonobjects.get(beanname);
   if (singletonobject == null) {
    if (this.singletonscurrentlyindestruction) {
     throw new beancreationnotallowedexception(beanname,
       "singleton bean creation not allowed while the 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);
    boolean newsingleton = false;
    boolean recordsuppressedexceptions = (this.suppressedexceptions == null);
    if (recordsuppressedexceptions) {
     this.suppressedexceptions = new linkedhashset<exception>();
    }
    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 != null_object ? singletonobject : null);
  }
 }

这个方法的第一句object singletonobject = this.singletonobjects.get(beanname)从一级缓存中取数据,肯定是null。随后就调用的beforesingletoncreation方法。

?
1
2
3
4
5
protected void beforesingletoncreation(string beanname) {
  if (!this.increationcheckexclusions.contains(beanname) && !this.singletonscurrentlyincreation.add(beanname)) {
   throw new beancurrentlyincreationexception(beanname);
  }
 }

其中就有往singletonscurrentlyincreation这个set中添加beanname的过程,这个set很重要,后面会用到。随后就是调用singletonfactory的getobject方法进行真正的创建过程,下面我们看下刚刚上文提到的真正的创建的过程createbean,它里面的核心逻辑是docreatebean.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
protected object docreatebean(final string beanname, final rootbeandefinition mbd, final object[] args) {
  // instantiate the bean.
  beanwrapper instancewrapper = null;
  if (mbd.issingleton()) {
   instancewrapper = this.factorybeaninstancecache.remove(beanname);
  }
  if (instancewrapper == null) {
   instancewrapper = createbeaninstance(beanname, mbd, args);
  }
  final object bean = (instancewrapper != null ? instancewrapper.getwrappedinstance() : null);
  class<?> beantype = (instancewrapper != null ? instancewrapper.getwrappedclass() : null);
 
  // allow post-processors to modify the merged bean definition.
  synchronized (mbd.postprocessinglock) {
   if (!mbd.postprocessed) {
    applymergedbeandefinitionpostprocessors(mbd, beantype, beanname);
    mbd.postprocessed = true;
   }
  }
 
  // eagerly cache singletons to be able to resolve circular references
  // even when triggered by lifecycle interfaces like beanfactoryaware.
  //关注点3
  boolean earlysingletonexposure = (mbd.issingleton() && this.allowcircularreferences &&
    issingletoncurrentlyincreation(beanname));
  if (earlysingletonexposure) {
   if (logger.isdebugenabled()) {
    logger.debug("eagerly caching bean '" + beanname +
      "' to allow for resolving potential circular references");
   }
   addsingletonfactory(beanname, new objectfactory<object>() {
    @override
    public object getobject() throws beansexception {
     return getearlybeanreference(beanname, mbd, bean);
    }
   });
  }
 
  // initialize the bean instance.
  object exposedobject = bean;
  try {
   populatebean(beanname, mbd, instancewrapper);
   if (exposedobject != null) {
    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);
   }
  }
 
  if (earlysingletonexposure) {
   object earlysingletonreference = getsingleton(beanname, false);
   if (earlysingletonreference != null) {
    if (exposedobject == bean) {
     exposedobject = earlysingletonreference;
    }
    else if (!this.allowrawinjectiondespitewrapping && hasdependentbean(beanname)) {
     string[] dependentbeans = getdependentbeans(beanname);
     set<string> actualdependentbeans = new linkedhashset<string>(dependentbeans.length);
     for (string dependentbean : dependentbeans) {
      if (!removesingletonifcreatedfortypecheckonly(dependentbean)) {
       actualdependentbeans.add(dependentbean);
      }
     }
     if (!actualdependentbeans.isempty()) {
      throw new beancurrentlyincreationexception(beanname,
        "bean with name '" + beanname + "' has been injected into other beans [" +
        stringutils.collectiontocommadelimitedstring(actualdependentbeans) +
        "] in its raw version as part of a circular reference, but has eventually been " +
        "wrapped. this means that said other beans do not use the final version of the " +
        "bean. this is often the result of over-eager type matching - consider using " +
        "'getbeannamesoftype' with the 'alloweagerinit' flag turned off, for example.");
     }
    }
   }
  }
 
  // register bean as disposable.
  try {
   registerdisposablebeanifnecessary(beanname, bean, mbd);
  }
  catch (beandefinitionvalidationexception ex) {
   throw new beancreationexception(mbd.getresourcedescription(), beanname, "invalid destruction signature", ex);
  }
 
  return exposedobject;
 }

createbeaninstance利用反射创建了对象,下面我们看看关注点3 earlysingletonexposure属性值的判断,其中有一个判断点就是issingletoncurrentlyincreation(beanname)

?
1
2
3
public boolean issingletoncurrentlyincreation(string beanname) {
  return this.singletonscurrentlyincreation.contains(beanname);
 }

发现使用的是singletonscurrentlyincreation这个set,上文的步骤中已经将beanname已经填充进去了,所以可以查到,所以earlysingletonexposure这个属性是结合其他的条件综合判断为true,进行下面的流程addsingletonfactory,这里是为这个bean添加objectfactory,这个beanname(a)对应的对象工厂,他的getobject方法的实现是通过getearlybeanreference这个方法实现的。首先我们看下addsingletonfactory的实现

?
1
2
3
4
5
6
7
8
9
10
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);
   }
  }
 }

往第三级缓存singletonfactories存放数据,清除第二级缓存根据beanname的数据。这里有个很重要的点,是往三级缓存里面set了值,这是spring处理循环依赖的核心点。getearlybeanreference这个方法是getobject的实现,可以简单认为是返回了一个为填充完毕的a的对象实例。设置完三级缓存后,就开始了填充a对象属性的过程。下面这段描述,没有源码提示,只是简单的介绍一下。

填充a的时候,发现需要b类型的bean,于是继续调用getbean方法创建,记性的流程和上面a的完全一致,然后到了填充c类型的bean的过程,同样的调用getbean(c)来执行,同样到了填充属性a的时候,调用了getbean(a),我们从这里继续说,调用了dogetbean中的object sharedinstance = getsingleton(beanname),相同的代码,但是处理逻辑完全不一样了。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 != null_object ? singletonobject : null);
 }

还是从singletonobjects获取对象获取不到,因为a是在singletonscurrentlyincreation这个set中,所以进入了下面的逻辑,从二级缓存earlysingletonobjects中取,还是没有查到,然后从三级缓存singletonfactories找到对应的对象工厂调用getobject方法获取未完全填充完毕的a的实例对象,然后删除三级缓存的数据,填充二级缓存的数据,返回这个对象a。c依赖a的实例填充完毕了,虽然这个a是不完整的。不管怎么样c式填充完了,就可以将c放到一级缓存singletonobjects同时清理二级和三级缓存的数据。同样的流程b依赖的c填充好了,b也就填充好了,同理a依赖的b填充好了,a也就填充好了。spring就是通过这种方式来解决循环引用的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.jianshu.com/p/16a44c25c9d9

延伸 · 阅读

精彩推荐