Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
499 views
in Technique[技术] by (71.8m points)

dependency injection - Dagger: Inject @Named strings?

EDIT 2018-02-08: Sample project demonstrating how to do this at https://github.com/ravn/dagger2-named-string-inject-example - Note: the whole source is in a single file!


I am looking at whether dagger can replace guice for us (as our deployment Java platform is slow).

I construct a map of configuration strings at runtime, which I would like to have dagger inject as needed.

E.g. If I have

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

and

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

I would like to have "value" injected in value.

The examples in the source code does not have any @Named usages. Just trying gives the following exception:

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)

How should I approach this?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...