26

编辑2018-02-08:演示如何在https://github.com/ravn/dagger2-named-string-inject-example上执行此操作的示例项目- 注意:整个源代码在一个文件中


我正在研究 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)

我应该如何处理这个?

4

2 回答 2

23

您必须在 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);
    }
}
于 2013-08-07T13:16:26.660 回答
23

It sounds like you have a Map<String, String> and you want to use something that binds these automatically to named strings. You cannot do that as automatically in Dagger as you can in Guice, since in Guice you can create a properties binder.

Dagger requires knowledge of all of your bindings at compile-time, in order to do the analysis to ensure that all bindings and dependencies are satisfied

That said, you could do something like this - it is more boiler plate, but it is legit.

@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");
  }
  ...
}

This will allow for all of the bindings you need to be created, but to be satisfied from runtime values. The keys, however, are known at compile time (and must be, since you're using @Named("string literal") in your code anyway. Heck, if you have defined your property names and defaults as constant strings you can even do:

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

It is more boiler plate, but Dagger has, while trying to eliminate much boiler plate, preferred compile-time analysis over absolute boiler plate reduction. That said, I'll propose a feature that will improve this situation, auto-generating a module for system properties from a known list, or some such. I think even this boiler plate can be reduced.

于 2013-08-07T13:51:54.350 回答