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?
The @Named annotation is good for identifying which provider to be used when we are trying to inject the dependency of the same type.
Declaring Dependencies Inject annotation to identify which constructors and fields it is interested in. Use @Inject to annotate the constructor that Dagger should use to create instances of a class.
Dagger 2 Components are the counterpart to Guice Injectors so the way to do this in Dagger 2 would be to specify the object whose field you want to inject at runtime as an injection site and request injection from the component.
With the @Inject annotation on the constructor, we instruct Dagger that an object of this class can be injected into other objects. Dagger automatically calls this constructor, if an instance of this class is requested.
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.
You have to define a provider in the dagger module for your @Named instance.
@Provides @Named("foo") String provideFoo() { return "foo string"; }
Then you can inject the named instance in your constructor or using field injection in your dependent class.
public class Thermosiphon { @Inject @Named("foo") String fooString; @Inject public Thermosiphon(Heater heater) { System.out.println("value of fooString is " + fooString); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With