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:
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)
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)
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:
Keep a reference to the service in the instantiating class and set it on the instance created.
Avoid dynamic instantiation altogether and pre-inject all the possible classes that can be used.
Implement the service as a traditional service with message passing, e.g. network, IPC.
Any thoughts or ideas are appreciated.