【问题标题】:What is the purpose of the getter methods in Components in Dagger 2?Dagger 2 组件中的 getter 方法的目的是什么?
【发布时间】:2017-05-19 05:53:55
【问题描述】:

我正在尝试了解 Dagger 2 中的组件。这是一个示例:

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

    void inject(InjectionSite injectionSite);

    Foo foo();

    Bar bar();   

}

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

【问题讨论】:

标签: dependency-injection dagger-2


【解决方案1】:

Dagger 是一种连接对象及其依赖关系图的方法。作为直接调用构造函数的替代方法,您可以通过从 Dagger 请求实例来获取实例,或者通过提供您希望注入 Dagger 创建的实例的对象来获取实例。

让我们创建一家咖啡店,这取决于 Provider<Coffee> 和 CashRegister。假设您已将它们连接到一个模块中(可能是 LightRoastCoffee 和 DefaultCashRegister 实现)。

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&lt;Coffee&gt; 以提供 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 或服务用例的成员注入,或任何其他类型的混合搭配。

(注意: 除了用作对象图的入口点之外,称为 provision 方法 的无参数 getter 也可用于公开组件依赖项的绑定,因为David Rawson 在other answer 中描述。)

【讨论】:

  • 感谢您的回答。如果您认为它错误或具有误导性,我会删除我的。我一直在思考如何在 Dagger 2 中表达这些想法,对我来说,谈论在 Dagger 2 中发布与组件的绑定“感觉”是正确的,即使这通常是你会与模块相关联的东西。 Dagger 2 中的组件似乎介于 Injector 和 Module 之间,许多适用于模块的点也适用于组件。我找不到可以参考的规范来源,所以我尝试了一个答案。
  • @DavidRawson 我不会说你的值得删除;有多种方法可以处理具有不同背景的不同人的相同概念。不过,对我来说,“发布”方面仅适用于组件依赖项,因为所有组件方法都与 Guice Injector 上的方法具有一对一的映射关系(injectMembers 用于 void 方法,getInstance/@ 987654332@ 用于工厂方法,createChildInjector 用于子组件等)和 Guice 一样,您可以依赖注入对象,如 MembersInjector&lt;T&gt;Provider&lt;T&gt; 而无需“发布”它们。
  • 谢谢 - 关于依赖注入对象而不“发布”绑定的观点是恰当的。我没想到。我将阅读更多内容,然后编辑我的答案。
【解决方案2】:

在依赖组件中的使用

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

说明:

在 Dagger 2 中编写组件时,会将包含 @Provides 方法的模块组合在一起。这些@Provides 方法可以被认为是“绑定”,因为它们将抽象(例如,类型)与解析该类型的具体方法相关联。考虑到这一点,Foo foo() 方法使组件能够将其对 Foo 的绑定公开给依赖组件。

示例:

假设Foo 是一个应用程序Singleton,我们希望将它用作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();
}

【讨论】:

  • 好的,我想我可能会得到这个......我正确理解了您的 示例 2,到目前为止,我认为这是绑定的唯一用途。至于 Example 1 以及为什么我的代码仍然在没有绑定的情况下编译...首先,我使用的是子组件而不是组件依赖项。其次,我的代码中从来没有像@Provides DependsOnFoo dependsOnFoo(Foo foo) { return new DependsOnFoo(foo); } 这样的东西。我从未在您对我的问题的回答中看到这一点,也没有以某种方式推断出这一点。希望我最终得到的结果没有任何问题。
  • 我想我知道这一点更好一些,我的代码(如图所示 [stackoverflow.com/questions/41444511/…)基于您的回答工作得很好的原因可能是因为我使用的是子组件而不是组件依赖项?这是唯一的解释,但我不是匕首专家......
  • @Ricardo 是的,听起来不错——子组件的工作方式不同。我想我们最终是出于不同目的而交谈,因为我的回答是使用依赖组件。如果您按原样使用了我对该问题的回答,那么您需要发布 OkHttpClient,因为它被用作依赖组件中的依赖项。
  • 很高兴我们能解决所有问题。非常感谢。你真的帮助我更多地理解了 Dagger :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-21
  • 1970-01-01
  • 2018-11-04
  • 2011-02-07
相关资源
最近更新 更多