在Guice中创建钻石对象图(没有单例)

时间:2017-03-08 06:10:12

标签: java guice

我正在使用Guice进行依赖注入,并希望创建一个如下所示的对象图:

   d1        d2
  /  \      /  \
b1    c1  b2    c2
  \  /      \  /
   a1        a2
  • a1和a2表示A类等的实例。
  • A类取决于B和C类
  • B类和C类都依赖于D类
  • 但是,B和C中使用的D的实例应该相同

所以我想创建A类的两个实例,每个实例都有这个结构。 用例可以是D是由两个客户端B和C使用的某种数据对象(例如,DB连接),它们都在servlet A中使用。

最小Java类定义:

public class A {
    @Inject A(B b, C c) {}
}

public class B {
    @Inject B(D d) {}
}

public class C {
    @Inject C(D d) {}
}

public class D {}

现在我可以创建两个A:

的实例
Injector injector = Guice.createInjector(new DiamondModule());
A a1 = injector.getInstance(A.class);
A a2 = injector.getInstance(A.class);

但是,如果我这样做,我会得到四个不同的D实例,这不是我想要的。请注意,声明D单例将无济于事,因为那时我将只获得单个D实例。

我已经看到了以下问题,但是那个问题的答案在这里不起作用,或者至少我不知道如何:Guice inject single instance into multiple objects without using @Singleton

有谁知道如何解决这个问题?或者这个设计有点不对劲?或者这已经是我必须申报自己范围的实例?

解决方案

与此同时,我注意到这个问题与Dependency injection: Scoping by region (Guice, Spring, Whatever)重复,基于该问题的答案,我想出了一个解决方案。

基本思想是为每个钻石实例创建一个新的注入器,如下所示:

public class MainModule extends AbstractModule {
    @Override
    protected void configure() {
    }

    @Provides
    A createA() {
        return Guice.createInjector().getInstance(A.class);
    }
}

现在将D设为单例,其范围限定为特定的注入器。

// class D is a singleton
@Singleton
public class D {}

1 个答案:

答案 0 :(得分:2)

这被称为"机器人腿"问题(创建具有稍微不同实例的类似图表)并处理here

class LegModule extends PrivateModule {
   private final Class<? extends Annotation> annotation;

   LegModule(Class<? extends Annotation> annotation) {
   this.annotation = annotation;
}

@Override protected void configure() {
   bind(Leg.class).annotatedWith(annotation).to(Leg.class);
   expose(Leg.class).annotatedWith(annotation);

   bindFoot();
}

abstract void bindFoot(); 
}
public static void main(String[] args) {
   Injector injector = Guice.createInjector(
      new LegModule(Left.class) {
        @Override void bindFoot() {
          bind(Foot.class).toInstance(new Foot("leftie"));
        }
      },
      new LegModule(Right.class) {
        @Override void bindFoot() {
          bind(Foot.class).toInstance(new Foot("righty"));
        }
      });
 }