使用具有循环依赖关系的Guice

时间:2009-09-26 18:15:17

标签: dependency-injection guice

考虑这个简单的例子。

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

在此示例中,实例A了解实例B,实例B知道实例A.

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

4 个答案:

答案 0 :(得分:9)

你的例子根本不是问题,因为你是直接构建B的。但是如果你想要由Guice创建A和B,则一个或两个应该是一个接口。你可以这样做:

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也可以处理此注入(通过代理)。无论如何,这在一个简单的情况下工作......我想可能有更复杂的循环依赖,它无法处理。无论如何,当然,消除循环依赖是更可取的。

答案 1 :(得分:5)

回答第一个问题“如何使用Guice实例化实例A”:您只需将@Inject添加到构造函数中:

class A {
   private final B b;

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

这是有效的,因为用于创建A的API没有循环依赖关系。只要需要创建或注入A对象,Guice就会使用A构造函数。

如果您的问题是如何使用Guice创建一个用于创建对象的API具有循环依赖关系的对象,请参阅this blog post by Misko Hevery(如Yury的答案中所述)。

答案 2 :(得分:4)

答案是在代码中存在循环依赖时不应使用依赖注入框架。

因此,您必须事先重构代码。据我所知,紧耦合类有两种解决方案:将两个类合并为一个或引入新类并将常用逻辑移入其中(详细看here

答案 3 :(得分:3)

我认为NamshubWriter的提议并不是很有价值。我认为在Guice中,构造函数应该完成一件事:将参数分配到字段中。如果您还需要做任何其他事情,请将其放入工厂或提供商处。

在这种情况下,我们需要A的提供者。提供者可以直接调用新的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);
}

I made an extended version of the circular dependency injection in Guice where A and B are hidden behind interfaces.