使用Dagger进行嵌套/递归注射

时间:2014-02-11 06:03:34

标签: java android dagger

使用Dagger时,有哪些方法可以在也通过注入实例化的对象上自由/简单地实例化@Inject字段。

例如,下面的代码会将类型为Bar的对象注入给定的Foo对象。它将以显示的两种方式之一执行此操作。但是,每个Bar对象的Sly字段与该行为不匹配。

public class Foo {
  @Inject Bar bar;

  public String getValue() {
    return "Foo's bar value: " + bar.getValue();
  }
}

public class Bar {
  @Inject Sly sly;

  public String getValue() {
    return "Bar's sly value: " + sly.getValue();
  }
}

狡猾

public class Sly {
  public String getValue() {
    return "Hey!";
  }
}

模块

@Module(
    injects = {
        Foo.class,
        Bar.class
    }
)
public class ExampleTestModule {
  @Provides
  Bar provideBar() {
    return new Bar();
  }

  @Provides
  Sly provideSly() {
    return new Sly();
  }
}

测试

public void testWorksWithInject() {
  Foo foo = new Foo();
  ObjectGraph.create(new ExampleTestModule()).inject(foo);
  assertEquals("...", foo.getValue()); // NullPointerException
}

public void testWorksWithGet() {
  Foo foo = ObjectGraph.create(new ExampleTestModule()).get(Foo.class);
  assertEquals("...", foo.getValue()); // NullPointerException
}

在任何一种情况下,Bar's Sly都没有被实例化/ @Injected。当然,Dagger允许构造函数注入,这解决了问题。我想知道是否有替代方法将这些类塞入构造函数的参数列表中。什么对你有用?

1 个答案:

答案 0 :(得分:9)

所以这里的问题是Bar上有@Inject Sly,但是你在@Provides方法中提供了Bar。 @Provides方法覆盖默认的实例化行为,因此您告诉Dagger实例化“new Bar()”并将其作为Bar的规定的实现返回。

您可以做的最简单的事情就是删除provideBar()方法,因为它是不必要的。如果具体类型具有@Inject构造函数或@Inject字段,则Dagger将注入其依赖项并创建它,除非它具有不可访问的构造函数或没有@Inject的参数化构造函数。但是上面的Bar {}类以上的情况完全适用于隐式绑定,而不使用@Provides方法。

如果由于某种原因需要更改默认行为,您仍然可以在@Provides方法中创建它,但必须手动传入注入的值。但是,@ Provide方法本身可以通过向@Provides方法本身添加参数来注入。所以你可以这样做。

@Provides
Bar provideBar(Sly sly) {
  Bar bar = new Bar();
  bar.sly = sly;
  return bar;
}

@Provides方法负责正确配置实例,包括新建,分配,任何初始化逻辑等。

但是,根据上面的示例,简单的解决方案是简单地从模块中删除provideBar()并让Dagger自动初始化Bar。

Dagger 2似乎倾向于嵌套注入的一些不同的替代方案:请求组件,MembersInjector,或给Bar一个@Inject注释构造函数。

如果Bar有@Inject注释构造函数,那么你可以实现完全不变性:

class Bar {
  private final Sly sly;

  @Inject
  public Bar(Sly sly) {
    this.sly = sly;
  }
}

当您仅部分注入成员时,另一种替代方法是使用带有MembersInjector或component(MembersTestComponent?)的@Provides方法作为方法参数:

@Provides
Bar provideBar(MembersInjector<Bar> injector) {
  Bar bar = new Bar();
  injector.inject(bar);
  return bar;
}

不幸的是,提供MembersTestComponent参数会将模块耦合回组件并使解决方案不那么紧密。如果Bar包含Component提供的范围值(例如,在Jake Wharton的Devoxx 2014谈话中Tweeter内的用户),提供MembersInjector特别有用。