Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice submodules prevent inheriting bindings

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();
  }
}
like image 340
Matthew Madson Avatar asked Apr 26 '13 06:04

Matthew Madson


1 Answers

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!

like image 168
Matthew Madson Avatar answered Sep 28 '22 05:09

Matthew Madson