经验App:聚合职场优选内容程序员

Spring 对于循环依赖的处理

2019-04-16  本文已影响4人  谁说咖啡不苦

什么是循环依赖

循环依赖就是循环引用,就是两个或者多个bean相互之间的持有对方。

循环依赖的类型

我们先来看看如下的例子:

public class First {
    private  Second second;

    public void a () {
        second.b();
    }

    public Second getSecond() {
        return second;
    }
    public void setSecond(Second second) {
        this.second = second;
    }
}

public class Second {
    private Third third;

    public void b() {
        third.c();
    }

    public Third getThird() {
        return this.third;
    }

    public void setThird(Third third) {
        this.third = third;
    }
}

public class Third {
    private First first;

    public void c() {
        first.a();
    }

    public void setFirst(First first) {
        this.first = first;
    }

    public First getFirst() {
        return this.first;
    }
}

我们先来看看上面例子的两种循环依赖的情况。

构造器循环依赖

我们Bean的初始化是方式可以通过如下的xml过程.

<bean id="first">
  <constructor-arg index="0" ref="second"/>
</bean>
<bean id="second">
  <constructor-arg index="0" ref="third"/>
</bean>
<bean id="third">
  <constructor-arg index="0" ref="first"/>
</bean>

Spring容器将每一个正在创建的bean标识符放在一个“当前正在创建bean的池”中,bean标识符在创建的过程中将一直保持在这个池中,因此,如果在创建bean过程中发现自己已经在这个池中,将会抛出'BeanCurrentlyInCreationException'异常标识循环依赖无法处理;而对于创建完毕的bean将会从池中清除。
针对上述的这个例子,我们来理一下这个过程:

  1. Spring 容器创建"first"bean,首先会去池中查找是否已经创建当前的“first”bean,如果没有发现,进行调用构造器创建,这个时候,构造器需要参数second进行创建,因此,就要进行一个创建second的bean,同时,由于当前first是刚好处于一个正在创建的过程中,所以,我们要将first放入到我们这个“当前正在创建bean的池”中。
  2. 由第一步的构造器依赖,我们要接着创建second的bean,过程和first的一样,先进行一个判断是否存在second在池中,没有的话,就进行构造器创建,有依赖参数,就依赖参数的bean的创建,同时,将second放入池中。
  3. 同样的和上述的过程一样,但是,在这个时候,我们要进行构造器创建bean的时候,需要依赖first,那么我们按照上面的逻辑不走往下走的过程应该就是去创建first,那这个时候,这一步就回到了第一步的动作。但是,在第一步的时候,我们还是要进行一个检查判断是否在池中已经存在first。显然,这个时候,池中已经存在了我们刚刚创建的first的bean,因此,就会抛出BeanCurrentlyInCreationException这个异常了。

所以,我们从上面的过程可以得出一个结论,构造器的循环依赖是没法解决。

setter循环依赖

对于setter注入造成的循环依赖是通过Spring容器提前暴露完成构造器注入,但是未完成其他步骤(如,setter注入)的bean来完成的。简单的说就是,先调用一个无参构造函数,进行一个创建bean的实例,然后在创建完这个实例之后, 在调用相应的setter方法,将依赖填充进去。
对于上述例子,我们使用setter循环依赖的过程是这样的:

  1. Spring 容器创建单例"first"bean,首先根据无参构造器创建bean first,并暴露一个"ObjectFactory"用于返回一个提前暴露一个创建中的bean,并将first标识放入“当前正在创建bean的池”中,然后进行setter注入second。
  2. 创建bean second 和第一步同样的动作,然后进行setter注入third。
  3. 创建bean third和前面的步骤都一样,但是在进行setter注入first的时候,由于提前暴露了ObjectFactory工厂,从而使它能够返回提前创建中的一个bean。
  4. 最后在完成对third,second的setter注入。

所以,通过setter注入,是能够解决循环依赖的问题。


Spring对Bean的创建

通过上述的构造器循环依赖的创建bean和setter循环依赖的创建bean,我们可以发现,spring在处理的时候,走的是两种不同的方式进行一个处理。

上一篇下一篇

猜你喜欢

热点阅读