使用Dagger进行嵌套/递归注射 [英] Nested/Recursive Injections with Dagger

查看:206
本文介绍了使用Dagger进行嵌套/递归注射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

When using Dagger, what approaches will allow for free/easy instantiation of @Inject fields on objects that are also instantiated through injection.

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

For example, the code below will inject an object of type Bar into a given Foo object. It will do this in either of the two ways displayed. However, the Sly field of each Bar object is not matching that behavior.

Foo

public class Foo {
  @Inject Bar bar;

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

Bang

public class Bar {
  @Inject Sly sly;

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

Sly

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允许构造函数注入,这解决了问题。我想知道是否有替代方法将这些类塞入构造函数的参数列表中。什么对你有用?

In either case, Bar's Sly is not being instantiated/@Injected. Of course, Dagger allows for constructor injection, which solves the problem. I would like to know if there are alternatives to tucking those classes into the parameter list of constructors. What works well for you?

推荐答案

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

So the problem here is that Bar has @Inject Sly on it, but then you provide Bar in an @Provides method. @Provides methods override default instantiation behaviour, so you are telling Dagger to instantiate "new Bar()" and return it as a fulfillment of Bar's provision.

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

The easiest thing you can do is simply delete the provideBar() method, as it is unnecessary. If a concrete type has an @Inject constructor or an @Inject field, Dagger will inject its dependencies and create it, unless it has an inaccessible constructor or a parameterized constructor that has no @Inject. But your case above of class Bar{} above is entirely suitable for implicit binding, without use of @Provides methods.

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

If you need to alter that default behaviour for some reason, you can still create it in an @Provides method, but you must manually pass in the injected value. @Provides methods, however, can themselves be injected by adding parameters to the @Provides method itself. So you could do this.

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

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

A @Provides method takes all responsibility for properly provisioning the instance, including newing, assignments, any initialization logic, etc.

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

But, given your example above, the easy solution is to simply delete provideBar() from your module and let Dagger automatically initialize Bar.

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

There are some different alternatives which Dagger 2 seems to favor for nested injection: ask for a component, MembersInjector, or give Bar an @Inject annotated constructor.

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

If Bar has an @Inject annotated constructor then you can achieve full immutability:

class Bar {
  private final Sly sly;

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

当你只是部分注射成员时,另一种选择是使用带有MembersInjector或component(MembersTestComponent?)的@Provides方法作为方法参数:

The other alternative when you are only partially injecting members is to use a @Provides method with a MembersInjector or component (MembersTestComponent?) as a method argument:

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

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

Providing the MembersTestComponent argument will unfortunately couple the Module back to your Component and make the solution less cohesive. Providing the MembersInjector is especially useful if Bar contains scoped values provided by the Component (e.g. user inside Tweeter in Jake Wharton's Devoxx 2014 talk).

这篇关于使用Dagger进行嵌套/递归注射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆