Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dagger: Inject @Named strings?

Tags:

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?

like image 347
Thorbjørn Ravn Andersen Avatar asked Aug 07 '13 09:08

Thorbjørn Ravn Andersen


People also ask

What does @named do in dagger?

The @Named annotation is good for identifying which provider to be used when we are trying to inject the dependency of the same type.

What does @inject do dagger?

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.

How do you inject value at runtime in dagger?

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.

What is inject annotation in dagger?

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.


2 Answers

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.

like image 146
Christian Gruber Avatar answered Sep 20 '22 06:09

Christian Gruber


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);     } } 
like image 41
jfrey Avatar answered Sep 20 '22 06:09

jfrey