Spring中的循环依赖

fas*_*ava 93 java spring

Spring如何解决这个问题:bean A依赖于bean B,而bean B依赖于bean A.

Ric*_*arn 71

Spring参考手册介绍了循环依赖是如何解决的.首先实例化bean,然后相互注入.

考虑这个课程:

package mypackage;

public class A {

    public A() {
        System.out.println("Creating instance of A");
    }

    private B b;

    public void setB(B b) {
        System.out.println("Setting property b of A instance");
        this.b = b;
    }

}
Run Code Online (Sandbox Code Playgroud)

和类似的类B:

package mypackage;

public class B {

    public B() {
        System.out.println("Creating instance of B");
    }

    private A a;

    public void setA(A a) {
        System.out.println("Setting property a of B instance");
        this.a = a;
    }

}
Run Code Online (Sandbox Code Playgroud)

如果你有这个配置文件:

<bean id="a" class="mypackage.A">
    <property name="b" ref="b" />
</bean>

<bean id="b" class="mypackage.B">
    <property name="a" ref="a" />
</bean>
Run Code Online (Sandbox Code Playgroud)

使用此配置创建上下文时,您将看到以下输出:

Creating instance of A
Creating instance of B
Setting property a of B instance
Setting property b of A instance
Run Code Online (Sandbox Code Playgroud)

注意,当a注入时b,a尚未完全初始化.

  • 这就是为什么Spring需要一个没有参数的构造函数;-) (24认同)
  • 如果您在bean定义中使用构造函数参数,那就没有了!(但在这种情况下,你不能有循环依赖.) (14认同)
  • 如果您尝试使用构造函数注入,则错误消息为`org.springframework.beans.factory.BeanCurrentlyInCreationException:创建名为'a'的bean时出错:请求的bean当前正在创建中:是否存在无法解析的循环引用? (4认同)

Ste*_*n C 41

正如其他答案所说,Spring只是处理它,创建bean并根据需要注入它们.

其中一个后果是bean注入/属性设置可能以与XML布线文件似乎暗示的顺序不同的顺序发生.因此,您需要注意,您的属性设置器不会执行依赖于已调用的其他setter的初始化.处理这个问题的方法是将bean声明为实现InitializingBean接口.这需要您实现该afterPropertiesSet()方法,这是您进行关键初始化的地方.(我还包括用于检查实际已设置重要属性的代码.)


jon*_*ejj 18

在我正在使用的代码库(100万+代码行)中,我们遇到了启动时间长,大约60秒的问题.我们得到了12000+ FactoryBeanNotInitializedException.

我所做的是在AbstractBeanFactory#doGetBean中设置一个条件断点

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;
}
Run Code Online (Sandbox Code Playgroud)

它在哪里destroySingleton(beanName)我用条件断点代码打印异常:

   System.out.println(ex);
   return false;
Run Code Online (Sandbox Code Playgroud)

显然,当FactoryBean涉及循环依赖图时会发生这种情况.我们通过实现ApplicationContextAwareInitializingBean并手动注入bean 来解决它.

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class A implements ApplicationContextAware, InitializingBean{

    private B cyclicDepenency;
    private ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        ctx = applicationContext;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        cyclicDepenency = ctx.getBean(B.class);
    }

    public void useCyclicDependency()
    {
        cyclicDepenency.doSomething();
    }
}
Run Code Online (Sandbox Code Playgroud)

这将启动时间缩短到大约15秒.

因此,不要总是认为spring可以很好地为您解决这些问题.

出于这个原因,我建议使用AbstractRefreshableApplicationContext #setAllowCircularReferences(false)禁用循环依赖关系解决,以防止将来出现许多问题.

  • 有趣的推荐.如果您怀疑*循环引用导致性能问题,我的反建议只会这样做.(通过尝试解决不需要修复的问题来打破不需要打破的东西将是一种耻辱.) (2认同)
  • 这是一个滑坡,维护地狱允许循环依赖,从循环依赖重新设计你的架构可能是非常棘手的,就像我们的情况.它对我们大致意味着我们在启动期间获得了两倍的数据库连接,因为sessionfactory涉及循环依赖.在其他情况下,由于bean被实例化12000次以上,可能会发生更多灾难性的事情.当然你应该写你的bean,以便他们支持销毁它们,但为什么首先允许这种行为? (2认同)

ska*_*man 12

它就是这样做的.它实例化ab,并注入每一个到另一个(使用其setter方法).

有什么问题?

  • @javaguy:不,它不会. (8认同)

小智 12

问题 - >

Class A {
    private final B b; // must initialize in ctor/instance block
    public A(B b) { this.b = b };
}


Class B {
    private final A a; // must initialize in ctor/instance block
    public B(A a) { this.a = a };
 }
Run Code Online (Sandbox Code Playgroud)

//引起:org.springframework.beans.factory.BeanCurrentlyInCreationException:创建名为"A"的bean时出错:请求的bean当前正在创建:是否存在无法解析的循环引用?

解决方案1 ​​ - >

Class A {
    private B b; 
    public A( ) {  };
    //getter-setter for B b
}

Class B {
    private A a;
    public B( ) {  };
    //getter-setter for A a
}
Run Code Online (Sandbox Code Playgroud)

解决方案2 - >

Class A {
    private final B b; // must initialize in ctor/instance block
    public A(@Lazy B b) { this.b = b };
}

Class B {
    private final A a; // must initialize in ctor/instance block
    public B(A a) { this.a = a };
}
Run Code Online (Sandbox Code Playgroud)


Rit*_*pta 10

Spring中的循环依赖:一个Bean对另一个Bean的依赖。Bean A \xe2\x86\x92 Bean B \xe2\x86\x92 Bean A

\n

解决方案:

\n
    \n
  1. 使用@Lazy注释
  2. \n
  3. 重新设计类依赖
  4. \n
  5. 使用 Setter/Field 注入
  6. \n
  7. 使用@PostConstruct注释
  8. \n
\n

欲了解更多详情,请查看

\n


ear*_*las 6

Spring参考:

你通常可以相信Spring做正确的事.它在容器加载时检测配置问题,例如对不存在的bean和循环依赖关系的引用.当实际创建bean时,Spring会尽可能晚地设置属性并解析依赖关系.


小智 6

Spring容器能够解析基于Setter的循环依赖关系,但在基于构造函数的循环依赖关系中提供运行时异常BeanCurrentlyInCreationException.在基于Setter的循环依赖的情况下,IOC容器处理它与典型情况不同,在典型情况下它将在注入之前完全配置协作bean.例如,如果Bean A在Bean C上依赖Bean B和Bean B,则容器在将C注入B之前完全初始化C,并且一旦B完全初始化,它将被注入A.但是在循环依赖的情况下,在完全初始化之前,将豆子注入另一个.


Sil*_*ent 5

假设A依赖于B,那么Spring将首先实例化A,然后实例化B,然后为B设置属性,然后将B设置为A。

但是,如果B也依赖于A怎么办?

我的理解是:Spring刚发现A已经被构造(构造函数执行),但是还没有完全初始化(并不是所有注入都完成了),嗯,它认为,没关系,A可以被完全初始化是可以容忍的,只要将其设置为-现在将A实例完全初始化为B。在B完全初始化之后,将其设置为A,最后,A现已完全启动。

换句话说,它只是提前将A暴露给B。

对于通过构造函数的依赖关系,Sprint只是抛出BeanCurrentlyInCreationException来解决此异常,请通过构造函数-arg方式将依赖于其他bean的bean的lazy-init设置为true。


who*_*ami 5

在这里清楚地解释.感谢Eugen Paraschiv.

循环依赖是一种设计气味,要么修复它,要么使用@Lazy作为依赖性,导致问题解决它.