【问题标题】:Dagger: Inject @Named strings?Dagger:注入@Named 字符串?
【发布时间】:2013-08-07 09:38:25
【问题描述】:

编辑 2018-02-08:在https://github.com/ravn/dagger2-named-string-inject-example 演示如何执行此操作的示例项目 - 注意:the whole source is in a single file


我正在研究 dagger 是否可以代替我们的 guice(因为我们部署 Java 平台很慢)。

我在运行时构造了一个配置字符串映射,我希望根据需要进行 dagger 注入。

例如如果我有

java.util.Map<String, String> map = new java.util.TreeMap<String, String>();
map.put("key", "value");

@Inject
Thermosiphon(Heater heater, @Named("key") String value) {
    this.heater = heater;
    System.out.println("value =" + value);
}

我想在价值中注入“价值”。

源代码中的示例没有任何@Named 用法。只是尝试给出以下异常:

Exception in thread "main" java.lang.IllegalStateException: Errors creating object graph:
  No binding for @javax.inject.Named(value=key)/java.lang.String required by class bar.Thermosiphon
    at dagger.internal.ThrowingErrorHandler.handleErrors(ThrowingErrorHandler.java:34)
    at dagger.internal.Linker.linkRequested(Linker.java:146)
    at dagger.ObjectGraph$DaggerObjectGraph.getInjectableTypeBinding(ObjectGraph.java:288)
    at dagger.ObjectGraph$DaggerObjectGraph.get(ObjectGraph.java:249)
    at app.CoffeeApp.main(CoffeeApp.java:20)

我应该如何处理这个问题?

【问题讨论】:

    标签: dependency-injection dagger


    【解决方案1】:

    听起来你有一个 Map 并且你想使用一些自动将它们绑定到命名字符串的东西。你不能像在 Guice 中那样在 Dagger 中自动执行此操作,因为在 Guice 中你可以创建属性绑定器。

    Dagger 需要在编译时了解所有绑定,以便进行分析以确保满足所有绑定和依赖项

    也就是说,你可以做这样的事情 - 它更像是样板,但它是合法的。

    @Module(library = true)
    public class PropertiesModule {
      public final Properties props;
    
      PropertiesModule(Properties props) {
        this.props = props;
      }
    
      @Provides @Named("property.one") String providePropertyOne() {
        props.getProperty("property.one", "some default");
      }
    
      @Provides @Named("property.two") String providePropertyTwo() {
        props.getProperty("property.two", "some other default");
      }
      ...
    }
    

    这将允许您创建所有需要的绑定,但要满足运行时值。但是,键在编译时是已知的(并且必须是,因为无论如何您都在代码中使用 @Named("string literal") 。哎呀,如果您已将属性名称和默认值定义为常量字符串,您甚至可以做:

      @Provides @Named(PROPERTY_NAME_CONSTANT) String a() {
        props.getProperty(PROPERTY_NAME_CONSTANT, PROPERTY_NAME_CONSTANT_DEFAULT);
      }
    

    它是更多样板,但 Dagger 在尝试消除大量样板的同时,更喜欢编译时分析而不是绝对样板减少。也就是说,我将提出一个可以改善这种情况的功能,即从已知列表或类似列表中为系统属性自动生成一个模块。我认为即使是这个样板也可以减少。

    【讨论】:

    • 尽管这最初看起来像是更多的工作,但我认为它可以集中记录模块化应用程序经常需要支持哪些属性,尤其是在我们的用例中。我会试一试的。
    • 在源代码中指定这些属性的列表并让注释处理器生成 getter 会非常好。我不需要默认值 - 如果错误消息中的键不存在,我需要运行时异常。
    • 是的,我认为我们需要一个代码生成解决方案——从属性列表中生成一个模块。
    • 我现在已经完成了一些原型设计,每个键所需的额外工作已经超过了在方法主体中指定确切行为的能力,并且有一个放置 javadoc 的中心位置。换句话说,你的建议比我自己的更能解决我的实际问题。谢谢。
    • 这是否曾导致 dagger 中的功能 [request]?
    【解决方案2】:

    您必须在 dagger 模块中为您的 @Named 实例定义一个提供程序。

    @Provides @Named("foo") String provideFoo()
    {
        return "foo string";
    }
    

    然后您可以在构造函数中注入命名实例或在依赖类中使用字段注入。

    public class Thermosiphon
    {
        @Inject @Named("foo") String fooString;
    
        @Inject public Thermosiphon(Heater heater)
        {
            System.out.println("value of fooString is " + fooString);
        }
    }
    

    【讨论】:

    • 我的字符串是在运行时读取的属性文件中设置的,并且可能包含任意值。每个可能的密钥都需要提供程序吗?
    • 目前,没有像 Guice 那样在运行时创建新的和任意绑定的方法——这会破坏 Dagger 严格的编译时分析要求。您必须提前知道对象图的结构,因此您必须提前拥有所有这些命名绑定。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-09-30
    • 2019-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多