How do I inject fields at runtime using Dagger 2? - java

I need to inject fields of an instance of one of my classes on-demand i.e., at runtime because I'm instantiating them on-the-fly.
I used to use Guice for this where I would call MembersInjector#injectMembers or Injector#injectMembers. How can I have something like this in Dagger 2?

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.
Let's say you have a CoffeeShop with fields you want to inject:
class CoffeeShop {
#Inject CoffeeMaker coffeeMaker;
CoffeeShop() {
//we're not using constructor injection here
//although we probably should be :/
}
}
You can specify CoffeeShop as an injection site inside a component and request injection from it:
#Component(modules = { CoffeeModule.class })
interface CoffeeComponent {
void inject(CoffeeShop coffeeShop);
}
So inside another class you can do something like this:
private CoffeeComponent coffeeComponent;
void initComponent() {
coffeeComponent = DaggerCoffeeComponent
.builder()
.coffeeModule(new CoffeeModule())
.build();
}
void makeCoffee() {
CoffeeShop coffeeShop = new CoffeeShop();
coffeeComponent.inject(coffeeShop); //inject members of coffeeShop
coffeeShop.makeCoffee();
}
Alternatively, you can define provision methods inside your Dagger 2 Components which will allow you to resolve instances of a class ad hoc.
If you look at Jeff Bowman's example in the linked question, you can see there is a Component like this:
#Component(modules = {/* ... */})
public interface CoffeeShopComponent {
CoffeeShop getCoffeeShop();
void inject(CoffeeService serviceToInject); // to be discussed below
}
Say you then have a CoffeeService. You can now call getCoffeeShop() to obtain arbitrary instances of CoffeeShop:
class CoffeeService extends SomeFrameworkService {
private CoffeeComponent coffeeComponent;
void initComponent() {
coffeeComponent = DaggerCoffeeComponent
.builder()
.coffeeModule(new CoffeeModule());
.build();
}
public CoffeeShop createCoffeeShop() {
return coffeeComponent.getCoffeeShop(); //equivalent to Injector.getInstance();
}
}

I don't know that Dagger2 supported #BindsInstance in #Subcomponent.Factory or #Component.Factory at the time when this question was posted but anyway for now, the best approach to inject an instance in runtime seems to be using those.
In Dagger2 tutorial, there is an example(https://dagger.dev/tutorial/10-deposit-after-login) to create a subcomponent receiving an instance.

Related

Dynamically injecting generic objects with guice

My current situation:
I want to inject the following class into my application:
public interface IConfigAccessor<T extends IConfig> {
...
}
ConfigAccessors are a proxy-objects, created dynamically at runtime. The creation of these object works as follows:
public class ConfigFactory implements IConfigFactory {
private final IConfigUpdater updater;
#Inject
public ConfigFactory(IConfigUpdater updater) {
this.updater = updater;
}
#Override
public <T extends IConfig> IConfigAccessor<T> register(final String configKey, final Class<T> configClass) {
ConfigCache<T> configCache = new ConfigCache<>(new SomeOtherThings(), configKey, configClass);
updater.register(configCache);
return new ConfigAccessor<>(configCache, configKey, configClass);
}
}
As you can see, to create these objects, I need to inject the ConfigUpdater and other depdencies. This means, that guice needs to be fully configured already.
To get the instance out of Guice, I use the following code:
IConfigFactory configClient = injector.getInstance(IConfigFactory.class);
IConfigAccessor<ConcreteConfig> accessor = configClient.register("key", ConcreteConfig.class)
How I want to inject them via Guice:
Currently, I can get the requried objects, but I have to manually pass them around in my application.
Instead, what I want to have is the following:
public class SomeClass {
#Inject
public SomeClass(#Config(configKey="key") IConfigAccessor<ConcreteConfig> accessor) {
// hurray!
}
}
What's the correct approach/technology to get this working?
After a lot of research, I'm feeling a bit lost on how to approach this topic. There are a lot of different things Guice offers, including simple Providers, custom Listeners which scan classes and identify custom annotations, FactoryModuleBuilders and more.
My problem is quite specific, and I'm not sure which of these things to use and how to get it working. I'm not even sure if this is even possible with Guice?
Edit: What I have so far
I have the following annotation which I want to use inside constructor paramters:
#Target({ ElementType.FIELD, ElementType.PARAMETER })
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectConfig {
String configKey();
}
Inside the module, I can bind a provider to IConfigAccessor (with the above annotation) as such:
bind(IConfigAccessor.class).annotatedWith(InjectConfig.class)
.toProvider(new ConfigProvider<>());
However, there are two problems whith this:
The provider cannot provide IConfigAccessor. To create such an instance, the provider would need an IConfigUpdater, but since I use 'new' for the provider, I can't inject it.
Inside the provider, there is no way to find out about the configKey used in the Annotation.
Second approach:
Let's assume that I already know all configurations and configKeys I want to inject during startup. In this case, I could loop over all possible configKeys and have the following binding:
String configKey = "some key";
final Class<? extends IConfig> configClass =...;
bind(IConfigAccessor.class).annotatedWith(Names.named(configKey))
.toProvider(new ConfigProvider<>(configKey, configClass));
However, problem (1) still resides: The provider cannot get an IConfigUpdater instance.
The main problem here is that you cannot use the value of the annotation in the injection. There is another question which covers this part:
Guice inject based on annotation value
Instead of binding a provider instance, you should bind the provider class, and get the class by injecting a typeliteral.
That way, your config factory can look like that:
public class ConfigFactory<T extends IConfig> implements IConfigFactory {
#Inject private final IConfigUpdater updater;
#Inject private TypeLiteral<T> type;
#Override
public IConfigAccessor<T> register(final String configKey) {
Class<T> configClass = (Class<T>)type.getRawType();
ConfigCache<T> configCache = new ConfigCache<>(new SomeOtherThings(), configKey, configClass);
updater.register(configCache);
return new ConfigAccessor<>(configCache, configKey, configClass);
}
}
And then SomeClass:
public class SomeClass {
#Inject
public SomeClass(ConfigFactory<ConcreteConfig> accessor) {
ConcreteConfig config = accessor.register("key");
}
}
Since SomeClass needs to know "key" anyway, this is not too much a change information-wise. The downside is that the SomeClass API now gets a factory instead of the concrete config.
[EDIT]
And here is someone who actually did inject annotated values using custom injection.

How to inject pojo dependencies using dagger 2?

I have a simple pojo class:
public class MySimpleClass {
private List<String> mDependency;
public MySimpleClass (List<String> dependency) {
mDependency = dependency;
}
}
And I'm trying to have it created using dependency injection using dagger 2. Right now I have a simple module and component for it:
#Module
public class MySimpleClassModule {
#Provides
MySimpleClass provideMySimpleClass(List<String> dependency) {
return new MySimpleClass(dependency);
}
}
#Component(modules={MySimpleClassModule.class})
public interface MySimpleClassComponent {
}
But I'm not sure how can I inject the List<String> dependency every time I need to create a new instance of MySimpleClass. In the above scenario, it seems I would actually need to add List<String> to the constructor of MySimpleClassModule and have a new instance of that module every time I need a new instance of MySimpleClass with a new List<String>. Is that correct? It seems like a lot of overhead in this particular case.
No, it is not.
I assume you got a compilation error with Dagger since from question it is not clear if you already have a module that provides this list of strings.
To fix that one you can simply:
#Module
public class MySimpleClassModule {
#Provides
List<String> provideListDependency() {
return Arrays.asList("One", "Two");
}
#Provides
MySimpleClass provideMySimpleClass(List<String> dependency) {
return new MySimpleClass(dependency);
}
}
If you think that providing this list should be part of a different module you can move it. The main thing, that Dagger during compilation was able to find how to get this dependency.
If you do not want to create this array over in over you can mark method as #Singlethon so dagger will cache it.
If you get to define the constructor of an object it is best to use an #Inject constructor. Dagger 2 will automatically know how to instantiate the object so you won't need a #Provides annotated method in a module.
public class MySimpleClass {
private List<String> mDependency;
#Inject
public MySimpleClass (List<String> dependency) {
mDependency = dependency;
}
}
Dagger will assume that the parameters of the constructor are dependencies and try to resolve them from the dependency graph. Note that you can only have one #Inject annotated constructor per class! If you cannot instantiate the object yourself (e.g. Android Activities/Fragments) you need to use field injection.
In your case it doesn't seem necessary to inject an empty list into MyClass. You can just instantiate the list in the constructor. However, when you want to inject MyClass into another object it will already be in the object graph.
class A {
String name;
#Inject
A(String name) {
this.name = name;
}
#Component
interface AComponent {
A getA();
#Component.Builder
interface Builder {
#BindInstance
Builder provideName(String name);
A build();
}
}

Using Guice, inject dependency in child class

I want to inject dependency into a parent class while instantiating the child class using guice. In the example below, I am trying to create an instance of TrainingCommandData while I want to be able to inject TelemetryServiceClient during runtime using Guice. How can I do this?
public class TrainingCommandData extends CommandData {
private Intent intent;
public TrainingCommandData(UserCommandResource userCommandResource, Intent intent) {
super(userCommandResource);
this.intent = intent;
}
}
public class CommandData {
private TelemetryServiceClient telemetryServiceClient;
private UserCommandResource userCommandResource;
#Inject
public void setTelemetryServiceClient(TelemetryServiceClient telemetryServiceClient) {
this.telemetryServiceClient = telemetryServiceClient;
}
public CommandData(UserCommandResource userCommandResource) {
this.userCommandResource = userCommandResource;
}
}
When you extend a class, guice will take care of the injection of parent dependencies for you.
So you just let Guice create an instance of TrainingCommandData for you and you automatically get the TelemetryServiceClient injected.
There are some problems with the above code though:
you need to put "#Inject" on your non-default constructor ... and of course guice must be able to create all parameters for you. If you only now these parameters at runtime, have a look at the assisted injection extension
Using setter injection is not a good choice in your use case ... why should your commanddata suggest that it is possible to set a new instance of the service at runtime? I would not provide setters but use field injection, or, if you dont like that, constructor injection.

Inheritance (Late Binding) via Dependency Injection in Java

I am using Spring DI to wire my components and I came across this issue.
I have a BaseService class which has multiple implementations. And the layer above it, has a builder which calls the service to get data to populate POJOs. Service implementation I need to call (ServiceA,ServiceB) changes according to the type of POJO I need to build.
In such case, how can I autowire the service, as it requires late binding the service. How can I tackle this kind of scenario? (Example in Spring DI would really help)
I read similar questions but could not find the answer. And I read that SOA patterns such as Service Host provide different solutions to exact use case.
Please help.
Thanks
How about using a FactoryBean:
public class BuilderFactory implements FactoryBean<Builder> {
#Autowired
private ApplicationContext appContext;
...
#Override
public Builder getObject() {
Builder builder = new Builder();
switch(something()) {
case "foo":
builder.service = new ServiceA();
break;
case "bar":
builder.service= new ServiceB();
break;
...
default:
//handle cases where it's unclear which type to create
}
return builder;
}
}
where Builder instances have a public/package-private field BaseService service that gets called in their getData(), buildPojos() and wherever other methods.
(you could also use static factory methods to instantiate Builder if you want this field to be private)
You can use ServiceLocatorFactoryBean. In your case you would do something like this:
public interface BaseServiceLocator {
BaseService lookup(String qualifier); //use whatever qualifier type makes sense here
}
<bean id="serviceLocatorFactoryBean"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface"
value="your.package.BaseServiceLocator" />
</bean>
Then your builder would look something like this:
public class Builder {
#Autowired
private BaseServiceLocator baseServiceLocator;
#Override
public YourReturnType businessMethod() {
SomeData data = getData();
BaseService baseService = baseServiceLocator(data.getType()); //here I am assuming that getType() is a String
//whatever
}
I had the same requirement in one of my projects. I used reflection to get the services according to the pojo requirement. This way there will be no static values even if you define new pojo and service in future you wont have to change any implementation.
I had named my pojos and Services similarly. ie
POJO Name:Pond5DownloadStrategy and ServiceName: Pond5DownloadStrategyService.
I defined all the services in spring. I had a DownloadStrategyFactory which had a single method
getService(Object obj). which is also instantiated as spring bean.
what getService method did is.
I get the POJO name as string using obj.getClass().getSimpleName() and then I append Service at the end. ex.
If I pass Pond5DownloadStrategy then I do AppContext.getBean("Pond5DownloadStrategyService");
Please look at my answer here.
Although is under spring batch topic it’s actually related to your question and the Strategy Design pattern.
StrategyA StrategyB are your ServiceA,ServiceB etc.
You need to use the StrategyLocator in your Builder class (in the original answer it’s equivalent is MyTaskelt). The look-up will be based on your pojo type.
strategy = strategyLocator.lookup(POJOs.class);
In the answer I suggested a PlugableStrategyMapper, but if you predefine all Servcies you can place them in a Map in the application-context.xml
For example, for manual binding:
public class Builder {
#Autowired
private Map<String, Service> services;
// Bind pojo classes to bean names.
private Map<Class<?>, String> binding;
public Service getService(Object object) {
return services.get(binding.get(object.getClass()));
}
public Map<Class<?>, String> getBinding() {
return binding;
}
public void setBinding(Map<Class<?>, String> binding) {
this.binding = binding;
}
}
However, manual binding could be repetitive so if you don't really need his flexibility, you could use a naming convention (#AmitChotaliya answer) or enforce the binding via Service method.
public interface Service {
Class<?> getTargetType();
}
public class Builder {
#Autowired
private Set<Service> services;
// Bind pojo classes to Services.
private Map<Class<?>, Service> binding = new ConcurrentHashMap<Class<?>, Service>();
#PostConstruct
public void init() {
for (Service service : services) {
binding.put(service.getTargetType(), service);
}
}
public Service getService(Object object) {
return binding.get(object.getClass());
}
}

Guice and interface that has multiple implementations

If I have interface Validator and multiple implementations for this interface. How can I inject any of the multiple implementations with Guice? Now I know that I can use following code to inject one, but it allows only one implementation:
public class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(Validator.class).to(OneOfMyValidators.class);
}
}
What I would like to do is:
Validator v1 = injector.getInstance(Validator1.class);
Validator v2 = injector.getInstance(Validator2.class);
Is it possible at all?
Short answer: binding annotations. They're basically a way of letting the depender give a hint that points towards a particular instance or implementation without requiring a dependency on the full concrete implementation class.
See:
https://github.com/google/guice/wiki/BindingAnnotations
For example, in the module, you might do:
bind(Validator.class).annotatedWith(ValidatorOne.class).to(OneOfMyValidators.class);
bind(Validator.class).annotatedWith(ValidatorTwo.class).to(SomeOtherValidator.class);
And in your constructor, you'd do:
#Inject
MyClass(#ValidatorOne Validator someValidator,
#ValidatorTwo Validator otherValidator) {
...
}
To get an annotated value straight from an Injector, you'll have to use the Guice Key class, like:
Validator v1 = injector.getInstance(Key.get(Validator.class, ValidatorOne.class));
On a side note, binding annotations are very useful for injecting runtime constants. See the comments for bindConstant in:
https://google.github.io/guice/api-docs/latest/javadoc/index.html?com/google/inject/Binder.html
I found this thread when looking for a solution for dynamically binding multiple implementations to an interface, similar to ServiceLoader in Java. The answer covers a more general case, but it can also be used to obtain a particular implementation from the set. Multibinder allows to bind multiple implementations to a type:
public class ValidatorsModule extends AbstractModule {
protected void configure() {
Multibinder<Validator> multibinder
= Multibinder.newSetBinder(binder(), Validator.class);
multibinder.addBinding().toInstance(new ValidatorOne());
multibinder.addBinding().toInstance(new ValidatorTwo());
}
}
//Usage
#Inject Set<Validator> validators;
Very similar to ejboy's proposal, but since you own different Validator classes, you can bind to the classes itself, not creating instances manually.
protected void configure() {
...
Multibinder<Validator> mb = Multibinder.newSetBinder(binder(), Validator.class);
mb.addBinding().to(Validator1.class);
mb.addBinding().to(Validator2.class);
mb.addBinding().to(Validator3.class);
...
}
Then viewed from the perspective of usage, e.g. by Constructor Injection:
class UseCase {
private Set<Validator> allOfThem;
#Inject
public UseCase(Set<Validator> allOfThem) {
this.allOfThem = allOfThem;
// e.g. iteratation
for (Validator oneOfThem : allOfThem) {
...
}
}
}
Kotlin
This is how we can do binding for multiple implementations for an interface
Class SomeModule : AbstractModule() {
override fun configure() {
val myBinder: Multibinder<MyInterface> = Multibinder.newSetBinder(binder(), MyInterface::class.java)
myBinder.addBinding().to(Implementation1::class.java)
myBinder.addBinding().to(Implementation2::class.java)
}
Usage
#Inject constructor(private val someVar:Set<#JvmSuppressWildcards MyInterface>)

Categories