I would like to accomplish the following without introducing annotations or using string key names, is it possible using Guice
? Also, introducing a third private module for MyService
and its ExecutorService
binding is less than ideal, because I'd like the ExecutorService to be a singleton for the entire app, injected not just in MyService
, but potentially other classes, say MyOtherService
for example.
public class Main {
public static void main(String[] args) {
final Injector injector = Guice.createInjector(new MyAppModule());
final MyService service = injector.getInstance(MyService.class);
service.printInternals();
// Ideally this would print something like:
// My Executor: ExecutorImplClass@1
// Red Executor: ExecutorImplClass@2
// Blue Executor: ExecutorImplClass@3
}
}
public class MyAppModule extends PrivateModule {
@Override
protected void configure() {
install(new RedModule());
install(new BlueModule());
bind(ExecutorService.class).toInstance(Executors.newSingleThreadExecutor());
bind(MyService.class).to(MyServiceImpl.class);
expose(MyService.class);
}
}
public class BlueModule extends PrivateModule {
@Override
protected void configure() {
bind(ExecutorService.class).toInstance(Executors.newSingleThreadExecutor());
bind(BlueService.class).to(BlueServiceImpl.class);
expose(BlueService.class);
}
}
public interface BlueService {
void printInternals();
}
class BlueServiceImpl implements BlueService {
private final ExecutorService executor;
@Inject
BlueServiceImpl(final ExecutorService executor) {
this.executor = executor;
}
@Override
public void printInternals() {
System.out.println("Blue Executor: " + executor);
}
}
RedModule, RedService and RedServiceImpl
all mirror their respective Blue*
classes.
Finally MyService
, which uses the Red
and Blue Services
as well as it's own ExecutorService
:
class MyServiceImpl implements MyService {
private final ExecutorService executor;
private final RedService red;
private final BlueService blue;
@Inject
MyServiceImpl(final ExecutorService executor, final RedService red, final BlueService blue) {
this.executor = executor;
this.red = red;
this.blue = blue;
}
@Override
public void printInternals() {
System.out.println("My Executor: " + executor);
red.printInternals();
blue.printInternals();
}
}
TL;DR: Isolate the Blue
and Red
Modules to their own injectors and create providers in your App's
module which call the getInstance()
method of the injectors to retrieve the services your app needs.
Now for how I came to the solution:
Private Modules can get you most of the way there, see my experimentation.
But...
Let's say I'm developing an application which uses a service to do something totally awesome.
public class MyAppModule extends PrivateModule {
bind(AwesomeService.class).to(AwesomeServiceImpl.class);
expose(AwesomeService.class);
}
Now the particular implementation of my AwesomeService
needs a few things to be as awesome as it is:
class AwesomeServiceImpl implements AwesomeService {
@Inject
AwesomeServiceImpl(BlueService blue, RedService red, ExecutorService executor) { ... }
}
It just so happens that some upstanding Internet denizen has created a standalone jar with Guice modules that provide both Red
and Blue
Services. So I'll add the jar to my classpath and modify MyAppModule
so that my AwesomeService
can use the third party Red
and Blue
Services:
public class MyAppModule extends PrivateModule {
install(new RedModule());
install(new BlueModule());
bind(AwesomeService.class).to(AwesomeServiceImpl.class);
expose(AwesomeService.class);
}
I also need an ExecutorService
for my AwesomeService
, so I'll go ahead and bind to an explicit instance for now:
public class MyAppModule extends PrivateModule {
install(new RedModule());
install(new BlueModule());
bind(ExecutorService.class).toInstance(Executors.newSingleThreadExecutor());
bind(AwesomeService.class).to(AwesomeServiceImpl.class);
expose(AwesomeService.class);
}
Ah, but damn, apparently my good Internet friend decided to expose not only the RedService
and BlueService
bindings that my AwesomeService
needs, but also an ExecutorService
that I don't want:
public final class BlueModule extends PrivateModule {
bind(ExecutorService.class).toInstance(Executors.newCachedThreadPool());
bind(BlueService.class).to(BlueServiceImpl.class);
expose(ExecutorService.class);
expose(BlueService.class);
}
public final class RedModule extends PrivateModule {
bind(ExecutorService.class).toInstance(Executors.newCachedThreadPool());
bind(RedService.class).to(RedServiceImpl.class);
expose(ExecutorService.class);
expose(RedService.class);
}
No problem, I'll just wrap his modules in a private module and expose only the services I care about:
public class MyAppModule extends PrivateModule {
install(new PrivateModule() {
install(new RedModule());
expose(RedService.class);
});
install(new PrivateModule() {
install(new BlueModule());
expose(BlueService.class);
});
bind(ExecutorService.class).toInstance(Executors.newSingleThreadExecutor());
bind(AwesomeService.class).to(AwesomeServiceImpl.class);
expose(AwesomeService.class);
}
Ahh, but damn again, my ExecutorService
binding is inherited by my private wrapper modules and is conflicting with the internal bindings defined in the RedModule
and BlueModule
. I guess I could annotate or Name my ExecutorService
in my AwesomeService
constructor, but what if I want that ExecutorService
to be a singleton shared all over my app, by 20, 30 or 40 different services. I'll have to pollute all my ExecutorService
injections with this annotation.
Or I suppose I could do some trickery, staggering the bindings and hiding the ExecutorService
so it doesn't conflict with the ExecutorService
that RedModule
and BlueModule
create, but this just seems wrong:
public class MyAppModule extends PrivateModule {
install(new PrivateModule() {
install(new RedModule());
expose(RedService.class);
});
install(new PrivateModule() {
install(new BlueModule());
expose(BlueService.class);
});
final Module myAppExecutorService = new PrivateModule() {
bind(ExecutorService.class).toInstance(Executors.newSingleThreadExecutor());
expose(ExecutorService.class);
};
install(new PrivateModule() {
install(myAppExecutorService);
bind(AwesomeService.class).to(AwesomeServiceImpl.class);
expose(AwesomeService.class);
});
expose(AwesomeService.class);
}
There must be a better way... and there is... Injectors!
public class MyAppModule extends PrivateModule {
private static final Injector blueInjector = Guice.createInjector(new BlueModule());
private static final Injector redInjector = Guice.createInjector(new RedModule());
@Override
protected void configure()
{
bind(ExecutorService.class).toInstance(Executors.newSingleThreadExecutor());
bind(MyService.class).to(MyServiceImpl.class);
bind(MyOtherService.class).to(MyOtherServiceImpl.class);
expose(MyService.class);
expose(MyOtherService.class);
}
@Provides
RedService getRedService()
{
return redInjector.getInstance(RedService.class);
}
@Provides
BlueService getBlueService()
{
return blueInjector.getInstance(BlueService.class);
}
}
Now the ExecutorService
that is bound and exposed in both the BlueModule
and RedModule
won't pollute my AwesomeService's
ExecutorService
, yet I can still get my hands on those juicy BlueService
and RedService
classes I so desperately want.
Hopefully this saves someone else some time in the future!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With