Dagger 2中组件中的getter方法有什么作用?

11

我正在尝试理解Dagger 2中的组件(Component)。以下是一个例子:

@Component(modules = { MyModule.class })
public interface MyComponent {

    void inject(InjectionSite injectionSite);

    Foo foo();

    Bar bar();   

}

我理解 void inject() 方法的作用。但是我不理解其他 Foo foo() getter 方法的作用。这些方法的目的是什么?


相关:组件和模块之间的区别解释 https://dev59.com/Op_ha4cB1Zd3GeqP3bhd#42616757 - David Rawson
2个回答

13

依赖组件中的使用方法

在依赖组件层级结构中使用,例如这个示例中,提供方法Foo foo()用于向依赖组件公开绑定。 “公开”意味着“使可用”,甚至可以是“发布”。请注意,方法本身的名称实际上是不相关的。一些程序员选择将这些方法命名为Foo exposeFoo()以使方法名称反映其目的。

说明:

在Dagger 2中编写组件时,您会将包含@Provides方法的模块分组在一起。这些@Provides方法可以被视为“绑定”,因为它们将一个抽象(例如,类型)与解析该类型的具体方式关联起来。有了这个理解,Foo foo()方法使组件能够向依赖组件公开其对Foo的绑定。

示例:

假设Foo是应用程序单例,并且我们想将它用作DependsOnFoo实例的依赖项,但是在作用域更窄的组件内。如果我们在MyDependentComponent的模块中编写一个简单的@Provides方法,那么我们将得到一个新实例。相反,我们可以编写以下内容:

@PerFragment
@Component(dependencies = {MyComponent.class }
           modules = { MyDependentModule.class })
public class MyDependentComponent {

    void inject(MyFragment frag);

}

而且这个模块:

@Module
public class MyDepedentModule {

    @Provides
    @PerFragment
    DependsOnFoo dependsOnFoo(Foo foo) {
        return new DependsOnFoo(foo);
    }
}

同时假设DependentComponent的注入点包含DependsOnFoo

public class MyFragment extends Fragment {
    
    @Inject DependsOnFoo dependsOnFoo

}

请注意,MyDependentComponent 只知道模块 MyDependentModule。通过该模块,它知道可以使用 Foo 的实例来提供 DependsOnFoo,但它不知道如何自己提供 Foo。尽管 MyDependentComponentMyComponent 的依赖组件,但这仍然发生。在 MyComponent 中的 Foo foo() 方法允许依赖组件 MyDependentComponent 使用 MyComponentFoo 绑定来注入 DependsOnFoo。如果没有这个 Foo foo() 方法,编译将会失败。

解决绑定的用法

假设我们想要获取 Foo 的实例,而不必调用 inject(this)。组件内部的 Foo foo() 方法可以允许这样做,就像您可以使用 Guice 的 Injector 或 Castle Windsor 的 Resolve 调用 getInstance() 一样。下图所示:

public void fooConsumer() {
    DaggerMyComponent component = DaggerMyComponent.builder.build();
    Foo foo = component.foo();
}

好的,我想我可能明白了... 我正确理解了你的Example 2,到目前为止,我认为这是绑定的唯一用法。至于Example 1以及为什么我的代码没有绑定就可以编译... 首先,我使用的是子组件而不是组件依赖项。其次,我从来没有像@Provides DependsOnFoo dependsOnFoo(Foo foo) { return new DependsOnFoo(foo); }这样的东西在我的代码中。我从你对我的问题的回答中也没有看到过,也没有推断出来。希望我最终得到的结果没有任何问题。 - rfgamaral
我认为我现在对这个问题有了更好的理解,而且根据你的回答,我的代码(如此[http://stackoverflow.com/questions/41444511/trying-to-get-my-head-around-dependency-injection-on-android-with-dagger2])能够正常工作,可能是因为我使用的是子组件而不是组件依赖?这是唯一的解释,但我并不是Dagger专家... - rfgamaral
1
@Ricardo 是的,那听起来是正确的 - 子组件的工作方式不同。我想我们最终产生了交叉目的的谈话,因为我的答案使用了依赖组件。如果您按原样使用我的回答,则需要发布 OkHttpClient,因为它被用作依赖项在依赖组件中使用。 - David Rawson
很高兴我们能够把一切都解决了。非常感谢,你确实帮助我更好地理解了Dagger :) - rfgamaral

9
Dagger是一种将对象图和它们的依赖关系连接起来的方法。与直接调用构造函数的方式不同,您可以通过从Dagger请求实例或提供希望使用Dagger创建实例的对象来获取实例。
让我们假设您已经在一个模块中将咖啡店(可能是LightRoastCoffee和DefaultCashRegister的实现)所依赖的Provider<Coffee>和CashRegister连接好了。
public class CoffeeShop {
  private final Provider<Coffee> coffeeProvider;
  private final CashRegister register;

  @Inject
  public CoffeeShop(Provider<Coffee> coffeeProvider, CashRegister register) {
    this.coffeeProvider = coffeeProvider;
    this.register = register;
  }

  public void serve(Person person) {
    cashRegister.takeMoneyFrom(person);
    person.accept(coffeeProvider.get());
  }
}

现在您需要获取该CoffeeShop的实例,但它只有一个包含其依赖项的两个参数构造函数。那么您该怎么做呢?简单:您告诉Dagger在生成的Component实例上提供一个工厂方法。
@Component(modules = {/* ... */})
public interface CoffeeShopComponent {
  CoffeeShop getCoffeeShop();

  void inject(CoffeeService serviceToInject); // to be discussed below
}

当您调用getCoffeeShop时,Dagger会创建Provider<Coffee>以提供LightRoastCoffee,创建DefaultCashRegister并将它们提供给Coffeeshop构造函数,然后返回结果。恭喜,您现在拥有一个完全连接的咖啡店。
现在,所有这些都是替代void注入方法的,这些方法需要一个已经创建的实例并将其注入其中:
public class CoffeeService extends SomeFrameworkService {
  @Inject CoffeeShop coffeeShop;

  @Override public void initialize() {
    // Before injection, your coffeeShop field is null.
    DaggerCoffeeShopComponent.create().inject(this);
    // Dagger inspects CoffeeService at compile time, so at runtime it can reach
    // in and set the fields.
  }

  @Override public void alternativeInitialize() {
    // The above is equivalent to this, though:
    coffeeShop = DaggerCoffeeShopComponent.create().getCoffeeShop();
  }
}

所以,你现在拥有两种不同的风格,它们都可以让你访问完全注入的对象图,而无需列出或关注它们需要哪些依赖项。你可以偏爱其中一种,或者喜欢顶层使用工厂方法和成员注入用于Android或服务用例,或者任何其他混合搭配方式。
(注意:除了作为进入对象图的入口之外,被称为“提供方法”的无参数getter还可用于公开组件依赖项的绑定,正如David Rawson在其他答案中描述的那样。)

谢谢你的回答。如果你认为我的回答是错误或误导性的,我会删除它。我已经思考了一段时间如何在Dagger 2中表达这些想法,对我来说,在Dagger 2中使用组件发布绑定“感觉”正确,尽管这通常是与模块相关联的。在Dagger 2中,组件似乎是注入器和模块之间的一种折衷方案,因此许多适用于模块的要点也适用于组件。我找不到一个权威的参考来源,所以我试着回答了一下。 - David Rawson
1
@DavidRawson 我不会说你的代码不值得删除;有多种方法来处理相同的概念,这些方法适用于具有不同背景的不同人。对我来说,“发布”方面仅适用于组件依赖项,因为所有组件方法都与 Guice 注入器上的方法具有 1 对 1 的映射关系(void 方法的 injectMembers,工厂方法的 getInstance/getProvider,子组件的 createChildInjector 等),就像使用 Guice 一样,您可以依赖注入对象,如 MembersInjector<T>Provider<T>,而无需“发布”它们。 - Jeff Bowman
谢谢 - 关于依赖注入对象而不“发布”绑定的观点是恰当的。我没有想到过。我会再多读一些,然后编辑我的答案。 - David Rawson

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