I generally code in .NET, and in a previous post I described how to use Microsoft Unity as the DI container in a ASP.NET MVC project. Also, the whole inspiration to dig into DI came from the book Dependency Injection in .NET. However, the first “real-life” project where I decided to let DI be the driving design principle happened to be a Java-project…
Anyway, that constraint turned out to be no problem whatsoever – thanks to the fact that the above mentioned book reaches way beyond the .NET framework in the description of DI techniques, and the fact that Google provides the terrific DI container Guice for Java.
One of the golden rules of DI is not to “new” up objects. Guice introduces the @Injection
annotation as an alternative to the new keyword. You can think of @Injection
as the new new
…
To prepare for constructor injection, you have to add the @Inject
annotation to your constructor:
1 2 3 4 |
@Inject public UserService(IRepository<User> repository) { super(repository); } |
Then, during object composition, all of the dependencies of that constructor will be automatically filled in by Guice.
You might argue that by adding this @Inject
annotation, you add a dependency in your UserService
class to Guice itself. However, Guice does support the standard JSR 330 annotations, so you actually don’t need to introduce Guice specific annotations in your code at all.
When composing the object graph, Guice uses bindings to map types to their actual implementations. The bindings define how dependencies are resolved during object composition. For example, to tell Guice which implementation to use for the IRepository<User>
interface, you will need linked binding. The below example maps the IRepository<user>
interface to the UserRepository
class using the to()
clause.
1 |
bind(new TypeLiteral<IRepository<User>>() {}).to(UserRepository.class) |
Note that because IRepository<User>
is a generic interface, an anonymous subclass of TypeLiteral
must be used in the declaration.
Now that the IRepository<user>
interface mapping is in place, you can use untargeted binding to bind the concrete UserService
class. An untargeted binding has no to()
clause:
1 |
bind(UserService.class); |
All the binding declarations must be gathered in the configure()
method of a module. A module is a class extending the AbstractModule
class:
1 2 3 4 5 6 7 8 |
public class UserServiceModule extends AbstractModule { @Override protected void configure() { bind(new TypeLiteral<IRepository<User>>() {}) .to(UserRepository.class) bind(UserService.class); } } |
The actual object composition is done using a so called injector:
1 2 3 4 5 |
public static void main(String[] args) { Injector injector = Guice.createInjector(new UserServiceModule()); UserService userService = injector.getInstance(UserService.class); ... } |
Obviously, this kind of code shall not be scattered all over your code base. All calls to Guice types – including the injector – should be isolated in some top-level component – the composition root (bootstrapper) of the application where the whole object graphs must be wired up.
I showed in a previous post how ASP.NET MVC has built-in support for the Unity DI-container. Likewise, Jersey – the Java library for building REST API’s – has seamless support for Guice so that you don’t have to manually call the getInstance()
method to create objects. Guice Servlet provides a utility that you can subclass in order to register your own ServletContextListener
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class GuiceServletConfig extends GuiceServletContextListener { @Override protected Injector getInjector() { return Guice.createInjector(new JerseyServletModule() { @Override protected void configureServlets() { bind(new TypeLiteral<IRepository<User>>() {}) .to(UserRepository.class).in(Singleton.class); bind(UserWebService.class); serve("/api/*").with(GuiceContainer.class); } }); } } |
To create a Guice injector you need to pass a JerseyServletModule
where you are overriding the configureServlets()
method. In here you must define the Guice bindings.
As most other DI containers, Guice also provides Lifetime management. The lifetime of objects can be handled through scopes. Guice supports the scopes singleton, session and request . Scopes can be configured in the bind statements using the in()
clause. Here is an example of setting the scope of the user repository to singleton:
1 2 3 |
bind(new TypeLiteral<IRepository<User>>() {}) .to(UserRepository.class) .in(Singleton.class); |
The final Guice feature I will mention is support for method interception through aspect oriented programming. This is a very powerful feature for solving cross-cutting concerns such as logging or authorization in your application. In a later post I will show how to implement role based authorization using aspect oriented programming in Guice.