If you come from a large Java shop, you've likely heard of or encountered Guice -- Google's lightweight dependency injection framework; analogous to Spring.
First time users of Guice will usually be starry eyed amazed at the ability to get type safe dependency and the seemingly modular way in which to bundle up your dependencies into Modules.
Much of the tutorials, guides and best practices found online though are targeted towards smaller codebases, and anyone in large Java shops will have likely hit the spaghetti of configuration and AbstractModule dependencies to get your code to boot up -- seemingly ruining the modularity of using a dependency injection framework.
This post is aimed at some best practice I've found for keeping AbstractModule composable and easy to understand.
If you don't want to read to the end just checkout my project on solving Guice deduplication -- guice-dedupe
Composition
The biggest hurdle large projects will face in Guice is that you'll want to keep Modules resuable and more importantly self-contained.
Consider the following example where I have a JSON class that is bound in a module, and two other modules want to make use of it.
public class Example { public static class JsonSerializer { //Implementation not important } public static class JsonModule extends AbstractModule { @Override protected void configure() { bind(JsonSerializer.class); } } public static class ModuleA extends AbstractModule { @Override protected void configure() { install(new JsonModule()); } } public static class ModuleB extends AbstractModule { @Override protected void configure() { install(new JsonModule()); } } }
We'd like to make use of the install option, so that a consumer can either use ModuleB or ModuleA and the necessary bindings are self-contained. The problem arises if ModuleA and ModuleB are used -- you'll be treated with a Multiple Binding Exception.
Many codebases, simply remove the installation of Module dependencies and move the mess of figuring out the right set of final modules you need at moment you try to create the injector. What a mess!
The way to solve this is to use Guice's built-in de-duplication code. For the most part it works out of the box, unless you are using @Provides in your modules.
Simply change all your existing AbstractModule to SingletonModule from the library guice-dedupe and you'll get modules that are fully self-contained now even with providers.