匕首 2:将用户输入的参数注入对象

2022-08-31 14:54:55

假设我有一个类Util,它接受一个对象 - 类 Validator 的一个实例。

由于我想避免在 Util 中实例化 Validator 类,因此我通过构造函数将其传入:

public class Util {

   @Inject
   public Util(Validator validator) {

   }


}

我有一个提供验证器实例的模块:

@Provides
@Singleton
Validator provideValidator() {
    return Validator.getInstance();
}

和 Util 类的一个实例:

@Provides
Util provideUtil(Validator validator) {
    return new Util(validator);
}

我有一个连接在一起的组件,它将为我提供一个Util实例:

Util getUtil()

所以在我的活动中,我可以这样称呼它:

Util myUtil = getComponent.getUtil();

所有这些都工作正常 - myUtil在实例化时具有适当的 Validator 类实例。

现在我想传入一个名为 address 的 String 变量(这是用户通过 UI 输入的)。我想更改构造函数,因此我传入了验证器的实例和用户输入的字符串:

@Inject
public Util(Validator validator, String address) {

}

我只是不知道如何传递第二个参数。有人能告诉我怎么做吗?

理想情况下,我想像这样实例化Util:

Util myUtil = getComponent.getUtil(txtAddress.getText());

答案 1

几周前,当我开始研究Dagger 2时,我遇到了和你一样的问题。我发现有关此(以及大多数其他匕首2相关问题)的信息很难获得,所以我希望这有帮助!

最基本的答案是你不能。你正在寻找的是一种叫做辅助注射的东西,它不是Dagger 2的一部分。其他一些依赖关系注入 (DI) 框架(如 Guice)确实提供了此功能,因此您可以研究这些框架。当然,仍然有一些方法可以使用Dagger 2做你想做的事情。

工厂 工厂 工厂

与 DI 结合使用执行要执行的操作的标准方法是使用工厂模式。基本上,您创建一个可注入的工厂类,该类采用运行时参数(如它提供的对象创建方法的参数)。address

在你的例子中,你需要一个 Dagger 2 在实例化时注入一个实例化,并提供一个创建 实例的方法。 应保留对 注入的实例的引用,以便它具有在方法中创建实例所需的一切。UtilFactoryValidatorcreate(String address)UtilUtilFactoryValidatorUtilcreate

对于许多这样的工厂来说,拧干代码可能很麻烦。你绝对应该看看AutoFactory,它减轻了一些负担。Guice的辅助注射似乎与Dagger 2 + AutoFactory非常相似(尽管语法糖更好)。

更多模块/组件

我怀疑在这种情况下这是您想要做的事情,但是您可以创建一个提供地址的模块(并实例化一个新组件)。您不必为每个可能的地址创建新的@Module类。相反,您可以只将地址作为参数传递给模块的构造函数。您可以按照 teano 的建议使用@BindsInstance注释来获得类似的结果。

我不确定这是否是反模式。对我来说,在某些情况下,这似乎是一个可以接受的路由,但前提是您实际使用相同的地址来初始化“许多”对象。您肯定不希望为每个需要注入的对象实例化新组件和新模型。它效率不高,如果你不小心,你最终会得到比没有Dagger更多的样板代码。

不要(总是)使用DI:注射剂与新药

在学习 DI 框架时,对我来说非常有用的一点是,我意识到使用 DI 框架并不意味着您必须 DI 来初始化所有对象。根据经验:注入您在编译时知道的并且与其他对象具有静态关系的对象;不注入运行时信息。

我认为这是一篇关于这个主题的好文章。它引入了“新药”和“注射剂”的概念。

  • 可注射物是 DI 图根附近的类。这些类的实例是您希望 DI 框架提供和注入的对象类型。管理器或服务类型对象是可注射对象的典型示例。
  • Newables是DI图边缘的对象,或者根本不是DI图的一部分。等是可更新对象的示例。IntegerAddress

从广义上讲,newables是被动对象,注入或嘲笑它们是没有意义的。它们通常包含应用程序中仅在运行时可用的“数据”(例如,您的地址)。Newables不应该保留对注射剂的引用,反之亦然(帖子的作者称之为“注射/可新分离”)。

实际上,我发现在注射剂和可注射剂之间做出明确的区分并不总是容易或可能的。不过,我认为它们是很好的概念,可以用作您思维过程的一部分。在将另一个工厂添加到您的项目之前,一定要三思而后行!

就您而言,我认为将其视为注射剂而将地址视为可注射物是有意义的。这意味着地址不应是类的一部分。如果你想使用实例,例如 验证/...地址,只需将要验证的地址作为参数传递给验证/...方法。UtilUtilUtil

2021年更新

自2.31版本的Dagger 2以来,还有一种辅助注射使用的机制。您可以在此处的文档中看到更多信息。(根据Jay Sidri的建议编辑。@AssistedInject


答案 2

您可以更改组件生成器以注入实例。请参见: https://google.github.io/dagger/users-guide#binding-instances

在您的情况下,您可以致电:

Util myUtil = DaggerMyComponent.builder().withAddress(txtAddress.getText()).build().getComponent().getUtil();

如果 MyComponent 被定义为:

@Component(modules = UtilModule.class)
interface MyComponent{

    MyComponent getComponent();

    @Component.Builder
    interface Builder {

        @BindsInstance Builder withAddress(@Address String address); //bind address instance

        MyComponent build();

    }
}

和 UtilModule:

@Module
class UtilModule{

    @Provides
    Util getUtil(Validator validator, @Address String address){ //inject address instance
        return new Util(validator, address);
    }

}

必须在@Component注释中传递给 MyComponent 模块的模块类中为验证程序提供@Inject带注释的构造函数或@Provides带注释的方法。

更新:

@Address是一个限定符,可以定义为:

@java.lang.annotation.Documented
@java.lang.annotation.Retention(RUNTIME)
@javax.inject.Qualifier
public @interface Address {}

也可以替换为@Named限定符,例如@Named(“地址”)。

有关限定符的详细信息,请参阅匕首指南


推荐