我有这样的情况。我看不到任何错误,但我没有得到我的结果。
@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;
}
}
这种类型的依赖注入是错误的吗?
任何人都可以帮助我。
答案 0 :(得分:4)
我会避免这种循环依赖,有几个原因可以做到这一点。
凌乱的构造函数是一个标志。它告诫我,我的班级正在变成一个巨型的,这是一个所有行业的杰克和无人的大师。换句话说,一个凌乱的构造函数实际上是一件好事。如果我觉得类的构造函数太乱了,我知道是时候对它做点什么了。
你会发现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
}
}