11

考虑这个简单的例子。

Class A {
   B b;
   A() {
       this.b = new B(this);
   }
}

在这个例子中,实例 A 知道实例 B,实例 B 知道实例 A。

我的问题是:如何用 Guice 实例化实例 A,即如何让 Guice 处理这种复杂的循环依赖关系?

4

4 回答 4

11

您的示例根本不是问题,因为您正在直接构建 B 。但是如果你希望 A 和 B 都由 Guice 创建,一个或两个应该是一个接口。你可以做:

public interface A { /* skipping methods */ }
public interface B { /* skipping methods */ }

public class AImpl implements A {
   private final B b;

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

public class BImpl implements B {
   private final A a;

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

即使AImplBImpl被限定为单例,Guice 也可以处理这种注入(通过代理)。无论如何,这在像这样的简单情况下都有效……我想可能会有更复杂的循环依赖项无法处理。无论如何,当然,消除循环依赖会更好。

于 2009-10-22T20:51:09.927 回答
5

答案是当你的代码中有循环依赖时,你不应该使用依赖注入框架

所以,你必须事先重构你的代码。据我所知,紧耦合类有两种解决方案:要么将两个类合并为一个,要么引入新类并将通用逻辑移入其中(详见此处

于 2009-09-27T07:21:29.707 回答
4

我认为 NamshubWriter 的提议不是很明确。我认为在 Guice 中,构造函数应该只做一件事:将参数分配给字段。如果您还有其他需要做的事情,请将其放入工厂或供应商中。

在这种情况下,我们需要 A 的提供者。提供者可以直接调用 new B(),但是我们会直接将 A 耦合到 B,这是我们一开始就试图避免的。所以我们通过工厂间接创建 B,guice 可以通过 assistedInject 为我们提供。这段代码运行和编译都很好,并且完全解耦了 A 和 B。

在现实场景中,您需要将 A 和 B 隐藏在接口后面以利用分离的优势。

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.FactoryProvider;

public class Try {
    public static void main(String[] args) {
        System.out.println(
                Guice.createInjector(new MyModule()).getInstance(A.class)
        );
    }
}

class MyModule extends AbstractModule {
    public void configure() {
        bind(A.class).toProvider(AProvider.class);
        bind(IBFactory.class).toProvider(
                FactoryProvider.newFactory(IBFactory.class, B.class));
    }
}

class A {
    B b;

    public void setB(B b) {
        this.b = b;     
    }
}

class B {
    A a;

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

class AProvider implements Provider<A> {

    private final IBFactory bFactory;

    @Inject
    AProvider(IBFactory bFactory) {
        this.bFactory = bFactory;
    }

    public A get() {
        A a = new A();
        a.setB(bFactory.create(a));
        return a;
    }
}

interface IBFactory {
    public B create(A a);
}

我在 Guice 中制作了循环依赖注入的扩展版本,其中 A 和 B 隐藏在接口后面。

于 2010-05-25T21:17:18.450 回答
4

要回答您的第一个问题“如何使用 Guice 实例化实例 A”:您可以简单地添加@Inject到构造函数中:

class A {
   private final B b;

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

这是有效的,因为用于创建的 APIA没有循环依赖。Guice 只会在需要创建或注入对象的A任何时候使用构造函数。A

如果您的问题是如何使用 Guice 创建对象,其中用于创建对象的 API 具有循环依赖关系,请参阅Misko Hevery 的这篇博文(如 Yury 的回答中所述)。

于 2009-09-27T22:27:55.983 回答