【问题标题】:Dagger 2: Injecting user inputted parameter into objectDagger 2:将用户输入的参数注入对象
【发布时间】:2016-09-27 18:09:21
【问题描述】:

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

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

public class Util {

   @Inject
   public Util(Validator 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 的字符串变量(这是用户通过 UI 输入的)。我想更改构造函数,因此我同时传入了一个 Validator 实例和用户输入的字符串:

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

}

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

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

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

【问题讨论】:

    标签: java android dagger-2


    【解决方案1】:

    从Dagger 2 2.31版本开始,就有了辅助注入的机制。我们可以为此使用@AssistedInject。您可以在文档here 中查看更多信息。

    示例:

    我们有一个类需要辅助注入:

    class MyDataService {
      @AssistedInject
      MyDataService(DataFetcher dataFetcher, @Assisted Config config) {}
    }
    

    我们使用@AssistedInject 表示我们的构造函数需要辅助注入,并使用@Assisted 标记哪些参数将被辅助注入。

    那么我们需要定义一个辅助注入的工厂

    @AssistedFactory
    public interface MyDataServiceFactory {
      MyDataService create(Config config);
    }
    

    最后,Dagger 将为我们生成这个工厂,我们将能够在需要使用依赖项的地方注入它。

    class MyApp {
      @Inject MyDataServiceFactory serviceFactory;
    
      MyDataService setupService(Config config) {
        MyDataService service = serviceFactory.create(config);
        // ...
        return service;
      }
    }
    

    【讨论】:

    • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
    【解决方案2】:

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

    最基本的答案是你不能。您正在寻找的是一种称为辅助注入的东西,它不是 Dagger 2 的一部分。其他一些依赖注入 (DI) 框架,例如 Guice,确实提供了这个功能,所以你可能会调查那些。当然,仍然有一些方法可以使用 Dagger 2 完成您想做的事情。

    工厂工厂工厂

    结合 DI 完成您想做的事情的标准方法是使用工厂模式。基本上,您创建了一个可注入工厂类,它将运行时参数(例如 address)作为它提供的对象创建方法的参数。

    在您的情况下,您需要一个 UtilFactory,Dagger 2 在实例化时将 Validator 注入其中,并提供一个方法 create(String address) 来创建 Util 的实例。 UtilFactory 应该保留对Validator 注入实例的引用,以便它拥有在create 方法中创建Util 实例所需的一切。

    编写许多此类工厂的代码可能很麻烦。你一定要看看AutoFactory,它减轻了一些负担。 Guice 的辅助注入似乎与 Dagger 2 + AutoFactory 非常相似(尽管语法糖更好)。

    更多模块/组件

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

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

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

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

    我认为this 是关于这个主题的好帖子。它引入了“newables”和“injectables”的概念。

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

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

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

    在您的情况下,我认为将Util 视为可注射而将地址视为新的会是有意义的。这意味着地址不应该是Util 类的一部分。如果您想使用 Util 的实例,例如验证/... 地址,只需将要验证的地址作为参数传递给验证/... 方法。

    2021 年更新

    从匕首2的2.31版本开始,也有了使用@AssistedInject的辅助注入机制。您可以在文档here 中查看更多信息。 (根据Jay Sidri的建议编辑。)

    【讨论】:

    • 很好学习区分:注射剂和新药。精彩的答案让我清楚地了解了我的依赖关系。
    • 最后一点需要多说,我真的很喜欢“新能源”这个词。我得借那个。
    • 关于第一个解决方案:如何将 Validator 依赖项放入 UtilFactory? UtilFactory 是否会成为需要 Util 实例的任何地方的“可注入”/依赖项,并在程序启动/初始化时通过 DI 框架“连接在一起”(在这种情况下获取验证器依赖项)?当工厂分散在程序周围时,这不是很奇怪吗?
    • 或者让我换种说法:有没有办法不将工厂作为依赖项添加到使用它的类中?
    • 如果不知道你的应用程序的细节,我会说是的,你需要对工厂对象的依赖。但是可以注入工厂。通过创建工厂类,您将可以/应该注入的所有内容与您想要在运行时添加的内容分开:对“可注入”的依赖项进入工厂类,对“newables”的依赖项作为参数传递给工厂方法。 Util 的创建具有某些依赖项这一事实不会改变。如果你认为你的类有太多的依赖,那可能意味着它们耦合得太紧了。
    【解决方案3】:

    您可以更改组件构建器以注入实例。见: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);
        }
    
    }
    

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

    更新:

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

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

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

    有关限定符的更多信息,请参阅Dagger Guide

    【讨论】:

    • @Address 是什么?
    • 在上面的答案中回答了。
    • “在上述答案中回答是什么意思”。答案四处移动。请解释@Address 的原件。需要先创建吗?它只是某处声明的注释还是有什么独特之处?
    • 是的,它是在dagger.dev/dev-guide 的限定符部分中声明的注释
    • 应该改变“MyComponent getComponent();”到“Util util();”在MyComponent接口中,调用逻辑为:“Util myUtil = DaggerMyComponent.builder().withAddress(...).build().util();”
    【解决方案4】:
    @Inject
    Usermodel uuser;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        userComponent dc = DaggeruserComponent.create();
        dc.injectMain(this);
    
        historymodel hm =  uuser.getHistorymodel();// get the models to pass user inputs 
    
        videoModel vm = uuser.getVideoModel();//  get the models to pass user inputs
    
        hm.setUid("userid ");
    
    
    }
    

    }

    【讨论】:

      【解决方案5】:

      在启动Module的时候,可以传递一些参数如下:

      public NetServiceModule(String baseUrl, boolean isLogEnabled, CookieJar cookieJar) {
          this.mBaseUrl = baseUrl;
          this.mIsLogEnabled = isLogEnabled;
          this.mCookieJar = cookieJar;
      }
      

      然后在“容器类”中获取组件:

      NetServiceComponent component = DaggerNetServiceComponent.builder()
                  .netServiceModule(new NetServiceModule(baseUrl, mIsLogEnabled, cookieJar))
                  .build();
          component.inject(this);
      

      通过Provides方法提供Injection,如果需要,可以通过一些参数生成:

      @Provides
      Retrofit provideRetrofit(OkHttpClient httpClient, GsonConverterFactory gsonConverterFactory, NetCallAdapterFactory netCallAdapterFactory) {
      
          return new Retrofit.Builder()
                  .client(httpClient)
                  .baseUrl(mBaseUrl)
                  .addConverterFactory(gsonConverterFactory)
                  .addCallAdapterFactory(netCallAdapterFactory)
                  .build();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-11
        • 1970-01-01
        • 2022-09-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多