处理CDI中的循环依赖

时间:2015-04-27 13:58:12

标签: java dependency-injection cdi

我有这样的情况。我看不到任何错误,但我没有得到我的结果。

@ApplicationScoped
public class A {

    private B b;


    @Inject
    public A(B b) {
        this.b = b;
    }
}

@Singleton
public class B {

    private A a;


    @Inject
    public B(A a) {
        this.a = a;
    }
}

这种类型的依赖注入是错误的吗?

任何人都可以帮助我。

4 个答案:

答案 0 :(得分:4)

我会避免这种循环依赖,有几个原因可以做到这一点。

评论this article

  

凌乱的构造函数是一个标志。它告诫我,我的班级正在变成一个巨型的,这是一个所有行业的杰克和无人的大师。换句话说,一个凌乱的构造函数实际上是一件好事。如果我觉得类的构造函数太乱了,我知道是时候对它做点什么了。

this one

  

你会发现A类需要B实例而B需要A实例的情况。这是循环依赖的典型情况,显然很糟糕。根据我的经验,解决方案是要么让B成为A的一部分,当两者非常依赖,他们真的应该是一个类。更常见的是,至少还有一个C级隐藏在那里,因此B不需要A而只需要C。

作为Oliver Gerke commented

  

特别是构造函数注入实际上会阻止您引入循环依赖项。如果你确实介绍了它们,你基本上会把这两个一方当成一个,因为你不能真正改变它,而不用冒险打破另一方,这在任何情况下都是一种设计气味。

以下是我可能会做的一个小例子。

public class A {

    private B b;

    @Autowired
    public A(B b) {
        this.b = b;
    }

    public void doSomeWork() {
        // WORK
    }

    public void doSomeWorkWithB() {
        b.doSomeWork();
    }
}

public class B {

    private A a;

    @Autowired
    public B(A a) {
        this.a = a;
    }

    public void doSomeWork() {
        // WORK
    }

    public void doSomeWorkWithA() {
        a.doSomeWork();
    }

}

重构后,它可能看起来像这样。

public class A {

    private C c;

    @Autowired
    public A(C c) {
        this.c = c;
    }

    public void doSomeWork() {
        // WORK
    }

    public void doSomeWorkWithC() {
        c.doSomeWorkThatWasOnA();
    }

}

public class B {

    private C c;

    @Autowired
    public B(C c) {
        this.c = c;
    }

    public void doSomeWork() {
        // WORK
    }

    public void doSomeWorkWithC() {
        c.doSomeWorkThatWasOnB();
    }

}

public class C {

    public void doSomeWorkThatWasOnB() {
        // WORK

    }

    public void doSomeWorkThatWasOnA() {
        // WORK
    }

}

答案 1 :(得分:2)

引自CDI Specification 1.2的第5部分:

  

容器需要支持bean中的圆形   依赖图,其中至少有一个bean参与其中   循环依赖链具有正常范围,如中所定义   正常范围和伪范围。容器不是必需的   支持每个bean参与的循环依赖链   在链中有一个伪范围。

ApplicationScoped是一个正常的范围,所以这个循环应该有效。

在您的示例中,类A无法代理,因为它缺少零参数构造函数。添加此构造函数(可能具有受保护或包可见性),您的样本可以毫无问题地部署。

答案 2 :(得分:0)

您还可以使用基于Setter的依赖注入来解决此问题。

答案 3 :(得分:0)

肯定有解决方案。让我自己quote

正确的解决方案是注入javax.enterprise.inject.Instance,其中T是要注入的类的类型。由于类型直接是Foo,因此对类型为Instance的对象调用get()方法可以保证始终注入正确的对象。这种方法非常有效,因为实例是由实现本身从容器动态获得的,并且只在需要时才实现。因此,依赖检索的责任留给您的代码 - 您的代码负责不进行无限循环。

 @Named
public class Foo implements Fooable{

@Inject
private Instance<Foo> foo;

public void executeFirst(){
foo.get().executeSecond();
}

@Transactional
public void executeSecond(){
//do something
}

}