Java on Guice
Guice 1.0 User’s Guide
Guice (pronounced “juice”) is an ultra-lightweight, next-generation dependency injection container for Java 5 and later.
Introduction
The enterprise Java community exerts a lot of effort toward wiring objects together. How does your web application get access to a middle tier service, or your service to the logged in user or transaction manager? You’ll find many general and specific solutions to this problem. Some rely on patterns. Others use frameworks. All result in varying degrees of testability and some amount of boilerplate code. You’ll soon see that Guice enables the best of all worlds: easy unit testing, maximal flexibility and maintainability, and minimal repetition.
We’ll use an unrealistically simple example to illustrate the benefits of Guice over some classic approaches which you’re probably already familiar with. The following example is so simple in fact that, even though it will show immediate benefits, we won’t actually do Guice justice. We hope you’ll see that as your application grows, Guice’s benefits accelerate.
In this example, a client depends on a service interface. This could be any arbitrary service. We’ll just call it Service.
public interface Service {
void go();
}
We have a default implementation of this service which the client should not depend directly on. If we decide to use a different service implementation in the future, we don’t want to go around and change all of our clients.
public class ServiceImpl implements Service {
public void go() {
…
}
}
We also have a mock service which we can use in unit tests.
public class MockService implements Service {
private boolean gone = false;
public void go() {
gone = true;
}
public boolean isGone() {
return gone;
}
}
Plain Old Factories
private ServiceFactory() {}
private static Service instance = new ServiceImpl();
public static Service getInstance() {
return instance;
}
public static void setInstance(Service service) {
instance = service;
}
}
Our client goes directly to the factory every time it needs a service.
public void go() {
Service service = ServiceFactory.getInstance();
service.go();
}
}
The client is simple enough, but the unit test for the client has to pass in a mock service and then remember to clean up afterwards. This isn’t such a big deal in our simple example, but as you add more clients and services, all this mocking and cleaning up creates friction for unit test writing. Also, if you forget to clean up after your test, other tests may succeed or fail when they shouldn’t. Even worse, tests may fail depending on which order you run them in.
public void testClient() {
Service previous = ServiceFactory.getInstance();
try {
final MockService mock = new MockService();
ServiceFactory.setInstance(mock);
Client client = new Client();
client.go();
assertTrue(mock.isGone());
}
finally {
ServiceFactory.setInstance(previous);
}
}
Finally, note that the service factory’s API ties us to a singleton approach. Even if getInstance() could return multiple instances, setInstance() ties our hands. Moving to a non-singleton implementation would mean switching to a more complex API.
Dependency Injection By Hand
While the client asked the factory for a service in our previous example, with dependency injection, the client expects to have its dependency passed in. Don’t call me, I’ll call you, so to speak.
private final Service service;
public Client(Service service) {
this.service = service;
}
public void go() {
service.go();
}
}
MockService mock = new MockService();
Client client = new Client(mock);
client.go();
assertTrue(mock.isGone());
}
Now, how do we connect the client with a service? When implementing dependency injection by hand, we can move all dependency logic into factory classes. This means we need a factory for our client, too.
private ClientFactory() {}
public static Client getInstance() {
Service service = ServiceFactory.getInstance();
return new Client(service);
}
}
Dependency Injection with Guice
Guice aims to eliminate all of this boilerplate without sacrificing maintainability.
With Guice, you implement modules. Guice passes a binder to your module, and your module uses the binder to map interfaces to implementations. The following module tells Guice to map Service to ServiceImpl in singleton scope:
public void configure(Binder binder) {
binder.bind(Service.class)
.to(ServiceImpl.class)
.in(Scopes.SINGLETON);
}
}
private final Service service;
@Inject
public Client(Service service) {
this.service = service;
}
public void go() {
service.go();
}
}
For Guice to inject Client, we must either directly ask Guice to create a Client instance for us, or some other class must have Client injected into it.
Guice vs. Dependency Injection By Hand
Guice enables you to specify scopes declaratively. For example, you don’t have to write the same code to store an object in the HttpSession over and over.
In the real world, you often don’t know an implementation class until runtime. You need meta factories or service locators for your factories. Guice addresses these problems with minimal effort.
When injecting dependencies by hand, you can easily slip back into old habits and introduce direct dependencies, especially if you’re new to the concept of dependency injection. Using Guice turns the tables and makes doing the right thing easier. Guice helps keep you on track.
More Annotations
public interface Service {
void go();
}
By default, Guice injects a new instance every time. If you want to specify a different scope, you can annotate the implementation class, too.
public class ServiceImpl implements Service {
public void go() {
…
}
}
Architectural Overview
Startup
public void configure(Binder binder) {
// Bind Foo to FooImpl. Guice will create a new
// instance of FooImpl for every injection.
binder.bind(Foo.class).to(FooImpl.class);
// Bind Bar to an instance of Bar.
Bar bar = new Bar();
binder.bind(Bar.class).toInstance(bar);
}
}
Creating an Injector entails the following steps:
- First, create an instance of your module and pass it to Guice.createInjector().
- Guice creates a Binder and passes it to your module.
- Your module uses the binder to define bindings.
- Based on the bindings you specified, Guice creates an Injector and returns it to you.
- You use the injector to inject an object.
Runtime
Each binding has a provider which provides instances of the necessary type. You can provide a class, and Guice will create instances of it for you. You can give Guice an instance of the type you’re binding to. You can implement your own provider, and Guice can inject dependencies into it.
Each binding also has an optional scope. Bindings have no scope by default, and Guice creates a new instance for every injection. A custom scope enables you to control whether or not Guice creates a new instance. For example, you can create one instance per HttpSession.
Bootstrapping Your Application
Your code should deal directly with the Injector as little as possible. Instead, you want to bootstrap your application by injecting one root object. The container can further inject dependencies into the root object’s dependencies, and so on recursively. In the end, your application should ideally have one class (if that many) which knows about the Injector, and every other class should expect to have dependencies injected.
For example, a web application framework such as Struts 2 bootstraps your application by injecting all of your actions. You might bootstrap a web service framework by injecting your service implementation classes.
Dependency injection is viral. If you’re refactoring an existing code base with a lot of static methods, you may start to feel like you’re pulling a never-ending thread. This is a Good Thing. It means dependency injection is making your code more flexible and testable.
If you get in over your head, rather than try to refactor an entire code base all in one shot, you might temporarily store a reference to the Injector in a static field somewhere or use static injection. Name the field’s class clearly though: InjectorHack and GodKillsAKittenEveryTimeYouUseMe come to mind. Keep in mind that you you’ll have to mock this class, and your unit tests will have to install an Injector here by hand, and remember to clean up afterwards.
Binding Dependencies
When injecting a dependency, Guice first looks for an explicit binding, a binding which you specified using the Binder. The Binder API uses the builder pattern to create a domain-specific expression language. Different methods return different objects depending on the context limiting you to appropriate methods.
For example, to bind an interface Service to a concrete implementation ServiceImpl, call:
void injectService(Service service) {
…
}
Note: In contrast to some other frameworks, Guice gives no special treatment to “setter” methods. Guice will inject any method with any number of parameters so long as the method has an @Inject annotation, even if the method is in a superclass.
DRY (Don’t Repeat Yourself)
We’ll use this syntax throughout the rest of the guide.
Annotating Bindings
.annotatedWith(Blue.class)
.to(BlueService.class);
void injectService(@Blue Service service) {
…
}
Creating Binding Annotations
Where did this @Blue annotation just mentioned come from? You can create such an annotation easily, although the standard incantation you have to use is unfortunately a little complex:
* Indicates we want the blue version of a binding.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@BindingAnnotation
public @interface Blue {}
Luckily, we don’t really have to understand it all just to use it. But for the curious, here’s what all this boilerplate means:
- @Retention(RUNTIME) allows your annotation to be visible at runtime.
- @Target({FIELD, PARAMETER}) is a courtesy to your users; it prevents @Blue from being applied to methods, types, local variables, and other annotations, where it would serve no purpose.
- @BindingAnnotation is a Guice-specific signal that you wish your annotation to be used in this way. Guice will produce an error whenever user applies more than one binding annotation to the same injectable element.
Annotations With Attributes
You can also bind to annotation instances, i.e. you can have multiple bindings with the same type and annotation type, but with different annotation attribute values. If Guice can’t find a binding to an annotation instance with the necessary attribute values, it will look for a binding to the annotation type instead.
Say for example we have a binding annotation @Named with a single string attribute value.
@Retention(RUNTIME)
@Target({ FIELD, PARAMETER })
@BindingAnnotation
public @interface Named {
String value();
}
class NamedAnnotation implements Named {Now we can use this annotation implementation to create bindings to @Named.
final String value;
public NamedAnnotation(String value) {
this.value = value;
}
public String value() {
return this.value;
}
public int hashCode() {
// This is specified in java.lang.Annotation.
return 127 * “value”.hashCode() ^ value.hashCode();
}
public boolean equals(Object o) {
if (!(o instanceof Named))
return false;
Named other = (Named) o;
return value.equals(other.value());
}
public String toString() {
return “@” + Named.class.getName() + “(value=” + value + “)”;
}
public Class<? extends Annotation> annotationType() {
return Named.class;
}
}
.annotatedWith(new NamedAnnotation(“Bob”))
.to(Bob.class);
Since identifying a binding by name is such a common use case, Guice provides a production-worthy implementation of @Named in com.google.inject.name.
Implicit Bindings
@Inject
Mixer(Concrete concrete) {
…
}
}
Injecting Providers
void injectAtm(Provider<Money> atm) {
Money one = atm.get();
Money two = atm.get();
…
}
As you can see, the Provider interface couldn’t get much simpler so it doesn’t get in the way of easy unit testing.
Injecting Constant Values
- Primitive types (int, char, …)
- Primitive wrapper types (Integer, Character, …)
- Strings
- Enums
- Classes
First, when binding to constant values of these types, you needn’t specify the type you’re binding to. Guice can figure it out from the value. For example, given a binding annotation named TheAnswer:
Converting Strings
If Guice still can’t find an explicit binding for one of the above types, it will look for a constant String binding with the same binding annotation and try to convert its value. For example:
Custom Providers
final Service service;
@Inject
WidgetProvider(Service service) {
this.service = service;
}
public Widget get() {
return new Widget(service);
}
}
Example: Integrating With JNDI
import com.google.inject.*;
import javax.naming.*;
class JndiProvider<T> implements Provider<T> {
@Inject Context context;
final String name;
final Class<T> type;
JndiProvider(Class<T> type, String name) {
this.name = name;
this.type = type;
}
public T get() {
try {
return type.cast(context.lookup(name));
}
catch (NamingException e) {
throw new RuntimeException(e);
}
}
/**
* Creates a JNDI provider for the given
* type and name.
*/
static <T> Provider<T> fromJndi(
Class<T> type, String name) {
return new JndiProvider<T>(type, name);
}
}
We can use our custom JndiProvider to bind DataSource to an object from JNDI:
import static mypackage.JndiProvider.fromJndi;
import javax.naming.*;
import javax.sql.DataSource;
…
// Bind Context to the default InitialContext.
bind(Context.class).to(InitialContext.class);
// Bind to DataSource from JNDI.
bind(DataSource.class)
.toProvider(fromJndi(DataSource.class, “…”));
Scoping Bindings
class MySingleton {
…
}
Specify annotations for custom scopes using Binder.bindScope(). For example, given an annotation @SessionScoped and a Scope implementation ServletScopes.SESSION:
Creating Scope Annotations
- Have a @Retention(RUNTIME) annotation so we can see the annotation at runtime.
- Have a @Target({TYPE}) annotation. Scope annotations only apply to implementation classes..
- Have a @ScopeAnnotation meta-annotation. Only one such annotation can apply to a given class.
For example:
* Scopes bindings to the current transaction.
*/
@Retention(RUNTIME)
@Target({TYPE})
@ScopeAnnotation
public @interface TransactionScoped {}
Eagerly Loading Bindings
Injecting Between Scopes
Development Stages
During development, Guice will load singleton objects on demand. This way, your application starts up fast and only loads the parts you’re testing.
In production, Guice will load all your singleton objects at startup. This helps catch errors early and takes any performance hits up front.
Your modules can also apply method interceptors and other bindings based on the current stage. For example, an interceptor might verify that you don’t use your objects out of scope during development.
Intercepting Methods
…
binder.bindInterceptor(
any(), // Match classes.
annotatedWith(Transactional.class), // Match methods.
new TransactionInterceptor() // The interceptor.
);
Try to shoulder as much of the filtering as is possible on the matchers rather than in the interceptor’s body as the matching code runs only once at startup.
Static Injection
For these situations, Guice supports injecting less accessible static members. For example, HTTP session objects often need to be serializable to support replication, but what if your session object depends on a container-scoped object? We can keep a transient reference to the object, but how do we look it up again upon deserialization?
We’ve found the most pragmatic solution to be static injection:
class User {
@Inject
static AuthorizationService authorizationService;
…
}
Optional Injection
If someone creates a binding for Formatter, Guice will inject an instance from that binding. Otherwise, assuming Formatter isn’t injectable itself (see Implicit Bindings), Guice will skip the optional member.
Optional injection applies only to fields and methods, not constructors. In the case of methods, if a binding for one parameter is missing, Guice won’t inject the method at all, even if bindings to other parameters are available.
Binding to Strings
…
bind(named(“bob”)).to(10);
Struts 2 Support
A Counting Example
public class Counter {
int count = 0;
/** Increments the count and returns the new value. */
public synchronized int increment() {
return count++;
}
}
final Counter counter;
@Inject
public Count(Counter counter) {
this.counter = counter;
}
public String execute() {
return SUCCESS;
}
public int getCount() {
return counter.increment();
}
}
class=”mypackage.Count”>
<result>/WEB-INF/Counter.jsp</result>
</action>
<html>
<body>
<h1>Counter Example</h1>
<h3><b>Hits in this session:</b>
<s:property value=”count”/></h3>
</body>
</html>
JMX Integration
See com.google.inject.tools.jmx.
Appendix: How the Injector resolves injection requests
The injector’s process of resolving an injection request depends on the bindings that have been made and the annotations found on the types involved. Here is a summary of how an injection request is resolved:
- Observe the Java type and the optional “binding annotation” of the element to be injected. If the type is com.google.inject.Provider<T>, perform resolution for the type indicated by T instead. Find a binding for this (type, annotation) pair. If none, skip to #4.
- Follow transitive bindings. If this binding links to another binding, follow this edge and check again, repeating until we reach a binding which does not link to any other binding. We are now at the most specific explicit binding for this injection request.
- If this binding specifies an instance or a Provider instance, we’re done; use this to fulfill the request.
- If, at this point, the injection request used an annotation type or value, we have failed and we produce an error.
- Otherwise examine the Java type for this binding; if an @ImplementedBy annotation is found, instantiate the referenced type. If a @ProvidedBy annotation is found, instantiate the referenced provider and use it to obtain the desired object. Otherwise attempt to instantiate the type itself.