Overview
Dependency Injection is a software pattern for writing high quality testable software. The Java world recently got a new framework named
Guice that was developed by Google and that provides an interesting alternative for people who fear (XML) configuration files.
Refresher on Dependency Injection
It's common for classes in object oriented environments, that they can't provide the requested functionality on their own, but need to use external services (classes connected using delegation). The easiest way is to create the required service is using the new() operator when needed. This will work at first but as soon as you want to replace the service with another implementation you run into problems and have to modify the code in many places (and rebuild the application). With modern refactoring tools this still wouldn't cause you a lot of headaches but when it comes to testing this approach really starts to hurt. Unit testing your object without testing the service gets impossible so a more dynamic approach for accessing the service becomes necessary.
Moreover, modern application design often prefers late-binding and loose-coupling, meaning, that some implementation details can be decided very late in the process, in many cases at the customer (e.g., which driver is uses, how a specific driver or module is configured). These configuration steps should also be feasible without refactoring of the application as this would mean a different code-basis for every customer!
You basically have two choices:
1.Using a
Service Locator that creates and returns the right service implementation depending on the context.
2. Someone else provides the right service; either during construction time or by using a factory pattern during runtime and calling setter-method(s).
Dependency Injection frameworks are particularly useful if you go for the second option. You first tell the framework which implementation to use for which interface. Whenever you then request an object of that interface you not only get the right implementation, but the requested objects dependencies, and their dependencies, and so on (recursively if needed), are also automatically resolved by the framework. It's considered best practice that the configuration of the framwork happens during the bootstrapping process and a single fully configured object is requested once from the framework. Most classes are not even aware of the injection framework (at least in the case of popular frameworks like
Spring).
Dependency Injection with Guice
If you're used to
Spring you will be surprised, that frameworks like
Guice don't necessarily need large, hard, in some cases hard to maintain, external configuration files. In Guice all your configuration is done in code and uses the annotation features of Java 1.5.
Configurations are separated in Modules by either implementing the Module interface or by inheriting from the AbstractModule class. Within the configure() method you then bind interfaces to their implementations.
public class MyModule implements Module {
public void configure(Binder binder) {
binder.bind(Service.class)
.to(ServiceImpl.class)
.in(Scopes.SINGLETON);
}
The example above is taken from the Guice documentation and binds the interface "Service" to the implementation "ServiceImpl". It also sets the service scope to be Singleton. Scopes deserve an article on their own, but basically they request a specific life cycle behavior for the object.
public class MyObject {
private final Service service;
@Inject
public Client(Service service) {
this.service = service;
}
}
A class that needs a service would just add a parameter to its constructor (or define a method that takes the service as an argument) and annotate it with the @Inject annotation.
public class FooApplication {
public static void main(String[] args) {
Injector injector = Guice.createInjector(
new MyModule(),
);
MyObject starter = injector.getInstance(MyObject.class);
// do something useful
}
}
During application startup an Injector is created with one or more modules as configuration. It is then used to request our object. Under the hood it will 1. create an instance of the class (depending on the scope), 2. look at all methods that we declared with @Inject and 3. supply the required services (back to 1).
External Configuration vs. Code-based Injection
There are good reasons not to use Guice especially if you like configuration files. It's true that you would have to recompile at least part of your code if you change the configuration. This would become a problem if you deploy your application to many targets that need to be configured differently. On the other hand configuration in code provides huge advantages: refactoring becomes much easier, your configuration files have to stay current otherwise your build will fail, in general readability is higher, and your development environment will help you with auto completion to configure your application much more quickly. The Module concept of Guice also allows you a more hybrid way: depending on some simple eg. property file, you could pick the right module during bootstrapping of the application. Which way you choose strongly depends on the needs of your application.
Summary
We just scratched the suface of Guice but showed the ease of use of the framework. Although it doesn't have as many features as Spring, it should be considered for projects where you want to reduce XML configuration files and need a slim fast dependency injection framework.