使用Dagger 2进行方法注入

44

我还没有找到一个好的解释/示例来说明如何使用Dagger 2进行方法注入。能否有人帮助我理解一下呢?

示例:

@Inject
public Dinner makeDinner(Pasta pasta, Sauce sauce) {
    mPan.add(pasta);
    mPan.add(sauce);
    return mPan.cookDinner();
}

如果我在我的方法上注释@Inject,那么我可以正确地假设方法签名中的参数将使用对象图中定义的对象进行注入吗?那么我如何在我的代码中使用这个方法呢?当我调用方法时,它仍然会期望我提供所有的参数,这有点违背了初衷。
更新:
所以我理解的是,如果我调用DinnerComponent.dinner(),那么Dinner对象将可用,假设我的DinnerComponent设置如下:
@Component(modules = DinnerModule.class)
public interface DinnerComponent {
    Dinner dinner();
}

我的DinnerModule设置如下:

@Module
public class DinnerModule {
    public DinnerModule() {}

    @Provides
    Pasta providePasta() { return new Pasta(); }

    @Provides
    Sauce provideSauce() { return new Sauce(); }
}

如果我想吃炸饭怎么办? 那么我们来介绍一下这种方法:

@Inject
public Dinner makeDinner(Pasta pasta, Sauce sauce) {
    mPan.add(pasta);
    mPan.add(sauce);
    return mPan.fryDinner();
}

在组件内部,我该如何指定哪个晚餐是哪个?


我不确定你从哪里得到这个例子,但是看起来你可能想了解一下辅助注入。 - Egor Neliuba
你可以使用@Named注解。 - eC Droid
不需要空的构造函数 public DinnerModule() {}。 - Marian Paździoch
2个回答

50
一种关于Dagger方法注入的基本区别,与您使用的方式相比,是Dagger方法注入是Dagger在构造或注入DI-ready对象时发送依赖项的另一种方式,这意味着@Inject-annotated方法旨在由Dagger在构造时调用一次,而不是从您自己的代码中调用。这使得您很难将makeDinner、fryDinner或任何具有有意义的副作用或返回值的其他方法注释为@Inject。相反,将方法注入视为构造函数样式注入后的机会。
(当然,您始终可以在方法级别上实践通用依赖注入,将依赖项传递到方法调用中,以便方法本身不必创建它们。但是,这不是Dagger在其方法注入定义中的含义,也不能帮助支持该案例。)
public class Chef {
  private Provider<Pasta> mPastaProvider;
  private Sauce mSauce;

  @Inject
  public void registerIngredients(     // can be named anything
      Provider<Pasta> pastaProvider,
      Sauce sauce) {                   // T and Provider<T> both work, of course
    mPastaProvider = pastaProvider;
    mSauce = sauce;
  }

  /* Non-@Inject */ public Dinner cookDinner() {
    mPan.add(mPastaProvider.get());
    mPan.add(mSauce);
    return mPan.cookDinner();
  }

  /* Non-@Inject */ public Dinner fryDinner() {
    mPan.add(mPastaProvider.get());
    mPan.add(mSauce);
    return mPan.fryDinner();
  }
}

在这种情况下,当您请求注入Chef实例时,Dagger将看到@ Inject注释的方法并使用来自Dagger图中的参数调用它。
无论是否可以构建Dagger,此方法都有效:除非您有@ Inject注释的构造函数或@ Provides方法,否则您将无法直接从Component获取Chef,但是您可以在Component上创建一个void方法,该方法接收已经构造的Chef实例。该组件方法将使用字段和方法注入为那些可能需要的Chef提供配料,提供者,可选项或Lazys。有关详细信息,请参见@ComponentMembersInjector文档。
请注意,Dinner在对象图中不可用!将@Inject添加到方法或字段中仅告诉Dagger,在注入过程的一部分中,它应该使用给定的依赖项填充该字段或调用该方法。如果您想在对象图中提供Dinner,则需要在Dinner构造函数上注释@Inject,或者在Module上放置一个@Provides或@Binds方法,并将其馈送到Component中。
为什么要使用方法注入?尽管构造函数注入是首选,并且允许类的字段为final,但考虑到通过反射创建对象的情况(例如Android中的Activities、Fragments和Views或可序列化对象),可以考虑使用方法注入。字段注入(Dagger填充@ Inject注释字段)也可以工作,但在某些情况下,您可能不希望公开@ Inject注释字段。在这些情况下,您可以通过在@ Inject注释的方法上进行注入来解决构造函数约束。同样,尽管我没有尝试过这个,但您可以利用类层次结构来标记带有@Inject的接口方法:这将确保无论您是否处于DI上下文中,都可以将某些依赖项作为其准备的一部分传递给对象。

2
在反复阅读了这个答案并亲自尝试之后,我认为我对方法注入终于已经掌握了。谢谢! - Vas
Provider<T> -> 什么是 Provider? - Marian Paździoch
@MarianPaździoch 在概念上,它是一个工厂,可以通过调用T get()来创建新的T实例。Dagger与Guice和Spring一样,使用标准接口javax.inject.Provider<T> - Jeff Bowman

17

使用 @Inject 标注一个方法会让 Dagger 在对象创建后立即执行该方法——也就是在构造函数调用后。当您需要完全构造的对象时,这非常有用。 本文 中有一个方法注入的示例。

您说得对,这个方法的参数将由 Dagger 提供,因此您不应该自行调用该方法。


“right after the constructor call” 这并不准确,在 @Inject 构造函数调用之后以及 @Inject 字段初始化之后才是正确的。 - Marian Paździoch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接