29

EDIT 2023-06-28: Unless you have very good reason not to, just use Spring Boot as your platform and use its auto wiring along with all its other goodies coming for free.

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?

Thorbjørn Ravn Andersen
  • 73,784
  • 33
  • 194
  • 347

2 Answers2

24

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.

Michael Alan Huff
  • 3,462
  • 3
  • 28
  • 44
Christian Gruber
  • 4,691
  • 1
  • 28
  • 28
  • Even though this initially seemed like more work I think that it can give the centralized documentation of what properties are supported frequently needed with modular applications, especially with our use cases. I will give it a try. – Thorbjørn Ravn Andersen Aug 08 '13 at 11:13
  • It would be really nice to specify a list of such properties in source and have the getters generated by the annotation processor. I do not need defaults - I need a runtime exception if not present with the key in the error message. – Thorbjørn Ravn Andersen Aug 08 '13 at 13:05
  • Yeah, I think we need a code-gen solution to that - something that generates a module from a list of properties. – Christian Gruber Aug 21 '13 at 00:13
  • I have now done some prototyping and the extra work needed for each key is more than paid for with the ability to specify exact behavior in the method body, and having a central place to put javadoc. In other words, your suggestion is a better solution to my actual problem than my own. Thanks. – Thorbjørn Ravn Andersen Aug 21 '13 at 06:11
  • 1
    Did this ever result in a feature [request] in dagger? – mfarrugi Feb 11 '17 at 00:43
  • @mfarrugi It is my opinion after doing this for quite a while that the "One provider for each configuration option"-approach is sound. Use constants for the `@named` strings everywhere. Use code to document what to do if a given configuration parameter is not given (fail fast is nice, defaults too). Use javadoc to explain the usage of the parameter on the provider which is close to the actual code using it. Doing this inside dagger loses those advantages. – Thorbjørn Ravn Andersen Feb 06 '18 at 13:47
23

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);
    }
}
jfrey
  • 707
  • 5
  • 8
  • My strings are set in property files read at runtime and may contain arbitrary values. Do I need a provider for each possible key? – Thorbjørn Ravn Andersen Aug 07 '13 at 13:23
  • 1
    For the moment, there is no at-runtime way to create new and arbitrary bindings as there is in Guice - that would defeat Dagger's strict compile-time analysis requirements. You must know your object graph's structure in advance, so you must have all of these named bindings in advance. – Christian Gruber Aug 07 '13 at 13:52