I would like to be able to change the Guice injections at runtime to support multiple injections based on user input. This is what I would like to achieve:
p
Your version is almost perfect as it is: You're going to need to inject some kind of object that returns one or the other based on code you write, and don't need assisted injection or anything like that. That said, you can skip some of the boilerplate:
public class DomainProvider {
// Just inject Providers directly without binding them explicitly.
@Inject @Earth Provider<IDao> earthDaoProvider;
@Inject @Mars Provider<IDao> marsDaoProvider;
public Provider<IDao> get(Domain domain){
switch (domain){
case EARTH:
return earthDaoProvider;
case MARS:
return marsDaoProvider;
}
}
public Provider<IDao> get(String domain){
Domain parsedDomain = Domain.valueOf(domain.toUpperCase());
return get(parsedDomain);
}
}
Your MyClass in that case would be exactly identical. Here, Provider
is either the one-method generic interface com.google.inject.Provider, or the equivalent builtin javax.inject.Provider that it extends. Read more about Guice Providers on the relevant Guice wiki topic.
bind(IDao.class).annotatedWith(Earth.class).to(EarthDao.class);
// You can now inject "@Earth IDao" and also "@Earth Provider<IDao>".
Basically, if you bind a key Foo
(to a class, provider, @Provides
method, or instance), you automatically get to inject either a Foo or Provider<Foo> with no additional work. Providers are also a great way to ensure that you get a new instance with every call to get
, if that's what you want; with your original, you'll always get the same instance of EarthDao or MarsDao for any given DomainProvider you inject. (If you have a scoped binding like @Singleton, Guice will respect that too; Provider just lets Guice get involved, rather than reusing a plain old Java reference.)
This means you can skip your custom EarthDaoProvider and MarsDaoProvider, unless you really need to perform any external initialization on them—at which point you'd probably be better off calling bind(EarthDao.class).toProvider(EarthDaoProvider.class)
so the preparation also happens when injecting EarthDao directly. You could also just have DomainProvider return an IDao instance directly by calling get
on the appropriate Provider, and be assured that it'll be a new instance every time.