Dependency injection in dynamically create instances

I have a class that is dynamically instantiated using reflection and which need to use a service. The service depends on a repository (JPA persistence) which in turn has its own dependencies (e.g. JPA, AkkaSystem).

I’ve created a RegistrationModule which extends AbstractModule. It’s enabled in the configuration file. It binds the Registration interface to the WebRegistration implementation of that same interface. I’ve also tried binding the repository used in the same module, but it seems to be redundant since it uses an @ImplementedBy annotation and is picked up automatically. Calling bind() or using annotations makes no difference. The service implementation uses constructor injection to get its repository dependency.

In the dynamically instantiated class I’ve tried both field injection and constructor injection and neither work – instance is always null. When I try to manually get the instance using the following code:

RegistrationModule module = new RegistrationModule();
Injector injector = Guice.createInjector(module); // Exception thrown here.
RegistrationService service = injector.getInstance(RegistrationService.class);

it throws the following exception

com.google.inject.CreationException: Unable to create injector, see the following errors:

  1. No implementation for akka.actor.ActorSystem was bound.
    while locating akka.actor.ActorSystem
    for the 1st parameter of persistence.DatabaseExecutionContext.(DatabaseExecutionContext.java:22)
    while locating persistence.DatabaseExecutionContext
    for the 2nd parameter of persistence.JPARegistrationRepository.(JPARegistrationRepository.java:21)
    at modules.RegistrationModule.configure(RegistrationModule.java:21)

  2. No implementation for play.db.jpa.JPAApi was bound.
    while locating play.db.jpa.JPAApi
    for the 1st parameter of persistence.JPARegistrationRepository.(JPARegistrationRepository.java:21)
    at modules.RegistrationModule.configure(RegistrationModule.java:21)

2 errors

If I attempt to bind these myself it fails saying they’re already bound, which makes sense and confuses me as to why the above exception says the implementations were not bound.

Am I not supposed to call into Guice like this? Is there a Play injector I should rather be using? I tried getting one from Play.current() but it says an application is not running, which makes no sense. I’m starting to believe manually injecting is the only possible approach here.

Moving away from binding and instead using the @Provides annotation in the module by first getting a provider reference to the repository the service needs, I’m making some progress in the sense that the provideRegistrationService method is getting called once from a controller that also uses the service and a service is instantiated properly and returned. But I’m not getting called again when the class that uses it is instantiated and thus the above Guice injection fails with the same exception. To be clear, field and constructor injection in the class are always null, and invoking the injector always produces an exception.

I believe that dynamically instantiating instances and dependency injection is not a good fit, but that Guice.createInjector() – or some other mechanism – should allow for an implementation to be looked up and returned, but please correct me if this is wrong.

As it stands I guess I have the following alternatives:

  1. Keep a reference to the service in the instantiating class and set it on the instance created.

  2. Avoid dynamic instantiation altogether and pre-inject all the possible classes that can be used.

  3. Implement the service as a traditional service with message passing, e.g. network, IPC.

Any thoughts or ideas are appreciated.

Thanks,

-Kaare

I found an alternative which I think is not too bad. It relies on using MapBinder in an AbstractModule to create key-instance mappings that can later be injected into the classes that need to get the instance for a certain key.

So in my case I did the following:

  • create an AbstractModule and in the configure method obtain the providers for the dependencies needed by each of the instances created.

  • in configure still, instantiate the instances needed, passing the respective provider references to the constructor for each class. Then add the instance using a well-known string identifier.

  • in a using class, inject a MapBinder instance so that the required instance can be retrieved using the aforementioned string key.

The instantiated classes had to be changed to lazily obtain the provided dependency since they aren’t available yet at instantiation.

So far this seems to be the best approach for my use case, which is to dynamically pick implementations to use based on the data set being processed.

I’m still interested to hear other people’s thoughts on the subject so please let me know if there are alternative approaches I should consider.