Google Guice

An agile lightweight dependency injection framework


April 25, 2008
URL:http://www.drdobbs.com/open-source/google-guice/207402198

Guice (pronounced "Juice") fully embraces modern Java language features and combines simplicity with stunning performance and developer"friendliness. This article is adapted from the book Google Guice: Agile Lightweight Dependency Injection Framework, by Robbie Vanbrabant. Presented courtesy of Apress, all rights reserved.


If you're in the business of creating software, you ultimately want to have maintainable software. You'll certainly agree with me that you spend more time maintaining software than writing software -- and that the maintainability you need doesn't come for free. It requires careful design and a well-defined process for testing and validating the application.

In your professional life, or even as a hobbyist, you've probably picked up the concept of unit testing. Basically, it's about testing little units of source code for validity. Being able to tell with one look at a bar (green or red) whether your code has the right side effects is valuable and will save you time. Unit testing is a no-brainer. Here, unit test examples will use JUnit 4.

I strongly believe that automated testing, like unit testing, is the best way to achieve software maintainability. With the right amount of test coverage, you can rest assured that, when you're making changes to the code, you won't break code somewhere else in the code base. You can simply write your tests, make your change, run the collected set of tests, and feel confident. Poorly designed applications are usually hard to test, which means well tested applications probably aren't too bad. You can write great software without automated testing-you can also win the lottery, but don't count on it.

So there you have it: Unit testing helps achieve maintainability. And what else can help you achieve that? Writing less code, of course! The less code you need to accomplish what you're trying to do, the less code you'll need to maintain. Obviously, you can't just randomly delete blocks of code, but in some cases, code doesn't really mean anything; it's just boilerplate to get you from point A to point B. Wouldn't it be nice if you could get rid of all that noise and focus on the stuff that matters? For lack of a better term, I call this the maintainability mission statement. This is not a complete list, but, among other things, maintainable code needs to be:

You probably already see where I'm going, but before we dive into Guice, let me illustrate how to accomplish these goals in a typical situation. When we're done with that, we'll throw Guice into the mix and dance on the ceiling.

Enter Guice

Like most open source software, Guice is freely downloadable on the Internet. However, before you download Guice, make sure that you have the following installed:

Once you have that, you're finally ready to slurp up some Guice:

  1. Go to http://code.google.com/p/google-guice.
  2. Click the Downloads tab.
  3. Download the file named guice-1.0.zip.
  4. Unzip the archive to a directory of your choice.

Inside the archive, you'll find the Guice API documentation and several JAR files. Now, we only need guice-1.0.jar, which holds the core framework. The other ones are either dependencies or extensions.

To follow along with the code examples in this chapter, create a new Java project in your IDE, and add guice-1.0.jar to the class path. Note that because some code listings only show the code relevant to the given section, some examples will not run as they are, but trying out the examples will definitely give you a good feel for how Guice works.

Preparing the Code

Take a look at Listing 1.

public class Chef {
private final FortuneService fortuneService;
  @Inject
    public Chef(FortuneService fortuneService) {
    this.fortuneService = fortuneService;
  }
  public void makeFortuneCookie() {
   new FortuneCookie(fortuneService.randomFortune());
  }
}
Listing 1: FortuneService that Gives Out Fortunes

Tagging a constructor with @Inject is essentially telling Guice where you want a dependency to be provided for you. Not only does this work for constructors but you can also apply @Inject to fields or methods. Which style you choose depends on the class's requirements and arguably your personal taste.

By "injection order is random," I mean that you should not rely on the order of injection. For example, if your class had two setters tagged with @Inject, you will never be sure which one will get called first by Guice. Guice often appears to use the order in which the methods were declared, but injection order can vary depending on the JVM you use, so assume the order is random.

Setter injection is a concept that is often misunderstood. If you're accustomed to using Spring, you've been probably using what it calls setter injection -- effectively, injection of JavaBean-style setters, which mean the methods you want to inject should be named setXXX, where XXX is the name of the single property that needs mutating. Guice, however, does not depend on this naming convention and can handle multiple parameters. The reasoning behind this is that it's also valid to want to inject methods that don't mutate a property but, for example, execute some kind of initialization logic right after object creation. But know that, as with Spring's setter injection, methods marked for injection get called with the appropriate values right after Guice creates the object for you. Once the object is fully initialized, Guice gets out of the way, and no more magic happens. So when you call the method yourself later, Guice does not magically provide some value for you.

What does work when it comes to injection is inheritance. If you subclass a class that features @Inject annotations, injection works as expected. First, the superclass gets injected, then the subclass. This only works for concrete classes though; you can't tag an implemented interface's members with @Inject and cross your fingers. Well you can, but it's not going to work.

It's also possible to inject static members or even members on objects that Guice didn't construct.

One final interesting point to note is that whichever type of injection you use, the target's visibility does not matter. Guice will inject anything tagged with @Inject whether it's private, package private, protected, or public.

Next up, I need to tell Guice that the chef wants a FortuneServiceImpl object when a FortuneService is requested.

Specifying an Implementation

Using the Module subclass I made previously, I can tell Guice which implementation to use for the Chef class's FortuneService, as illustrated in Listing 2:

public class ChefModule implements Module {
  public void configure(Binder binder) {
   binder.bind(FortuneService.class)
    .to(FortuneServiceImpl.class);
  }
}
Listing 2: Telling Guice Which FortuneService Service to Use

You can also subclass the AbstractModule abstract class instead of implementing Module. This abstract class implements Module itself and exposes a no-argument configure() method. To purists, using AbstractModule probably looks a bit scarier than implementing the actual interface, but it's more concise:

public class ChefModule extends AbstractModule {
   protected void configure() {
     bind(FortuneService.class).to(FortuneServiceImpl.class);
   }
}
Listing 3: AbstractModule Saves You Some Keystrokes

Bootstrapping

To start using Guice, you create an instance of Injector. This central Guice type takes a collected set of Module implementations and injects our beloved Chef class. To create the Injector, I use one of the factory methods on the Guice class, which is a simple static class, to serve as a starting point for creating injectors. This method, createInjector(...), takes a varargs argument, which means you can specify zero or more modules, separated by a comma. For Chef, I only need one. I'll have one cookie, please. Listing 4 to the rescue!

public class FortuneApplication {
public static void main(String[] args) {
     Injector i = Guice.createInjector(new ChefModule());
    Chef chef = i.getInstance(Chef.class);
  chef.makeFortuneCookie();
  }
}
Listing 4: Bootstrapping Guice and Creating Chef

FortuneServiceImpl doesn't have any dependencies itself, but if it did, Guice would have resolved its dependencies too. This recursive behavior allows you to use the Injector somewhere high up the stack in your application; Guice will then create the entire graph of dependencies below a requested object recursively.

The core Guice team has been reluctant to call this class a "container", as you would probably expect it to be named. Naming it Container would make you think that your objects sit somewhere in a container being managed, having a life cycle, and what not, which is not the way Guice does DI. The Injector injects your objects, and from then on, you're in control. Note: You will want to minimize the dependency on the Injector to avoid having a direct dependency on Guice. This is usually not very hard to do.

If you look around a bit, you'll see that createInjector(...) also has an overload that takes a Stage enumeration as the first parameter. The Stage of the Injector defines the mode of operation.

Using Stage.DEVELOPMENT means you'll have a faster start-up time and better error reporting at the cost of run-time performance and some up-front error checking. Stage.DEVELOPMENT is also the default. Using Stage.PRODUCTION on the other hand (shown in Listing 5), catches errors as early as possible and takes the full performance hit at start-up. But don't worry; Guice's overall performance is surprisingly good anyway. Just don't forget to switch on Stage.PRODUCTION for production code.

Injector i = Guice.createInjector(Stage.PRODUCTION, new ChefModule());
Listing 5: Specifying a Stage for the Injector

Choosing Between Implementations

The chef was obviously not pleased to figure out that the FortuneServiceImpl only had two fortunes to offer. To get some more variation in the messages, our chef subscribes to a second service, the MegaFortuneService, shown in Listing 6. Because the original subscription doesn't end until the end of the year, some way to choose between the two is necessary.

public class MegaFortuneService implements FortuneService {
private static final List<FortuneService> SERVICES =
   Arrays.<FortuneService>asList(
    new FunnyFortuneService(),
    new QuoteFortuneService()
    );
public String randomFortune() {
   int index = new Random().nextInt(SERVICES.size());
   return SERVICES.get(index).randomFortune();
   }
}
Listing 6: MegaFortuneService

Previously, the Guice knew which FortuneService to inject for Chef, because we had a binding in a Module implementation (see Listing 2). Common sense tells me to just add another binding for MegaFortuneService. Listing 7 shows what I came up with.

public class CommonSenseModule extends AbstractModule {
protected void configure() {
   bind(FortuneService.class).to(FortuneServiceImpl.class);
   bind(FortuneService.class).to(MegaFortuneService.class);
   }
}
Listing 7: Adding Another Binding: Does This Work?

You can easily modify the code in Listing 4 to create the Injector with the Module from Listing 7. However, when you do, you'll notice that Guice blows up at start-up. You'd see something like Listing 8.

Exception in thread "main" com.google.inject.CreationException:
Guice configuration errors:
1) Error at chapter2.CommonSenseModule.configure(CommonSenseModule.java:12):
A binding to chapter1.FortuneService was already configured at
chapter2.CommonSenseModule.configure(CommonSenseModule.java:11).
Listing 8: Oops, I Did It Again

Oh man, Guice doesn't like that. What did I forget? If you take a second look at the Chef constructor, shown in Listing 9, you'll see. Note: Guice's exceptions comprise a feature on their own. They go the extra mile and present you a human readable message and line numbers from your configuration code where appropriate. You'll be pleasantly surprised.

@Inject
 public Chef(FortuneService fortuneService) {
  this.fortuneService = fortuneService;
}
Listing 9: The Chef Constructor

Of course! Guice can't tell which FortuneService we need for Chef! So even if Guice would allow our CommonSenseModule bindings, using them would have never worked, because Guice has two FortuneService instances to choose from. I need some way to tell Guice, "Give me the MegaFortuneService!" without having to use the concrete implementations directly. Instead of letting you write more configuration, Guice solves this problem elegantly using binding annotations. Listing 10 provides an example.

@Inject
 public Chef(@Mega FortuneService fortuneService) {
  this.fortuneService = fortuneService;
 }
Listing 10: The Chef Constructor with a Binding Annotation

Now I can tell Guice which service is which in a module, as shown in Listing 11.

public class BindingAnnotationModule extends AbstractModule {
  protected void configure() {
   bind(FortuneService.class).to(FortuneServiceImpl.class);
   bind(FortuneService.class)
    .annotatedWith(Mega.class)
    .to(MegaFortuneService.class);
  }
}
Listing 11: A Module Using Binding Annotations

Again, my configuration remains highly readable, "Bind all requests for FortuneService annotated with @Mega to MegaFortuneService." One might hope that Guice provides these binding annotations for you. Alas, that wouldn't work for the same reason actors shouldn't go into politics: sometimes, you just have absolutely no idea what you're talking about. Similarly, Guice can't possibly know how to name your binding annotations, what their visibility should be, or where you would want to place them (in a constructor, method, or so on). That's why you need to create them yourself, much like in the @Mega example in Listing 12. Most binding annotations look very similar, so don't feel bad copying and pasting the boilerplate code. In fact, I even recommend doing so, because that means you're less likely to forget something.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@BindingAnnotation
public @interface Mega {}
Listing 12: @Mega Binding Annotation

Scary stuff.

To support this binding annotation functionality, Guice internally identifies all bindings with an instance of the Key class. A Key instance is a combination of a type (FortuneService) and an optional annotation type (@Mega). If you played around with the Injector a bit in the previous section, you probably noticed that the getInstance(...) method has an overload that takes a Key object instead of a Class one. There's only one Chef, but if you would like to get a MegaFortuneService directly, you would do so as in Listing 13.

injector.getInstance(Key.get(FortuneService.class, Mega.class));
Listing 13: Getting an Instance by its Key

If you call getInstance(...) with a Class, for example, Chef.class, Guice actually delegates to getInstance(Key) internally. So the two lines in Listing 14 are essentially the same.

injector.getInstance(Chef.class);
injector.getInstance(Key.get(Chef.class));
Listing 14: Get by Class or Key

Understanding that every single binding is internally represented by a Key object, including simple class bindings like Chef, will save you lots of time. Especially when you start using Guice's object lifetime support (scopes). I can't emphasize this enough, so repeat it after me: Every binding is represented by a Key, the whole Key and nothing but the Key, so help me Bob.

Implicit Bindings

In the fortunes example, you've probably noticed that I never made an explicit binding for Chef. This is because bindings don't always have to be explicit. As a rule of thumb, if there is no ambiguity, Guice will figure it out for you. This applies to concrete classes. As shown in Listing 15, I could have configured an explicit binding to Chef. This is kind of redundant, so I usually don't bind concrete classes explicitly.

public class ExplicitChefModule extends AbstractModule {
  protected void configure() {
  // no to(...) because you can't bind to the same class
  bind(Chef.class);
  }
}
Listing 15: Explicit Binding for Chef

Next to these implicit bindings, provided by Guice, you can also reduce configuration yourself when working with interfaces. Guice comes with an @ImplementedBy annotation that lets you specify which concrete class implementation to use for an interface. For example, Listing 16 shows the same FortuneService interface from Listing 2, now changed to use @ImplementedBy.

@ImplementedBy(FortuneServiceImpl.class)
public interface FortuneService {
  String randomFortune();
}
Listing 16: Using @ImplementedBy

By using @ImplementedBy, you can get rid of modules altogether. Whether that's a good idea or not, I'll leave up to you to decide. I usually stick to modules, because they allow you to change your application's configuration in a single line of code, by including or excluding certain modules when creating the Injector. However, you can use @ImplementedBy to specify a default implementation, and then override it in a Module implementation. That way, when creating an Injector without any modules, you'll always get a default implementation.

Note: Module configuration always takes precedence over annotation configuration.

Scoping

Guice's default behavior is to create a new instance of an object each time that object gets requested or injected. Scopes allow you to customize an object's lifetime. The canonical example is the built-in singleton scope, which makes sure only one instance of an object exists for a given Injector and internal Key. This is much, much better than using singletons manually, because this does not involve using static factory methods (or writing any code at all). But, as with any singleton, you'll have to make sure that your class is thread safe if you're going to access it from multiple threads.

To apply a scope to our FortuneService bindings, we specify either a scope annotation or an instance of the Scope class. For a singleton, these are Singleton.class and Scopes.SINGLETON, respectively. In Listing 17, I mix both of these styles (not recommended).

public class ScopedModule extends AbstractModule {
  protected void configure() {
    bind(FortuneService.class)
     .to(FortuneServiceImpl.class)
     .in(Singleton.class);
    bind(FortuneService.class)
     .annotatedWith(Mega.class)
     .to(MegaFortuneService.class)
     .in(Scopes.SINGLETON);
   }
}
Listing 17: Using Two Styles to Apply a Scope

You can also apply a scope by directly tagging your class with the @Singleton annotation, but as with @ImplementedBy, bindings in modules always take precedence.

The question that now is, "Do singletons load lazily or eagerly?" The short answer is that this will depend on the Injector's Stage, as I mentioned earlier. If you want to make sure that your singleton is created at application start-up (loaded eagerly), regardless of the Injector's Stage or the binding's usage, you can specify that as in Listing 18.

Tip: Stage.PRODUCTION loads singletons eagerly; Stage.DEVELOPMENT does not.

public class EagerSingletonModule extends AbstractModule {
  protected void configure() {
    bind(FortuneService.class)
     .to(FortuneServiceImpl.class)
     .asEagerSingleton();
    bind(FortuneService.class)
     .annotatedWith(Mega.class)
     .to(MegaFortuneService.class)
     .asEagerSingleton();
  }
}
Listing 18: Eager Singleton Loading

Loading singletons eagerly might be useful to execute initialization logic for your application. You can even create dependencies between them in such a way that you're sure they'll come up in the right order. I've only talked about the singleton scope for now, because it's the only scope that ships with core Guice (guice-1.0.jar). The 1.0 distribution also comes with a guice-servlet-1.0.jar archive containing the web-specific (servlet-specific) scopes: request and session.

Debunking Myths

Before finishing up, let's investigate a couple of common myths that haunt Guice. Some people who've seen the core Guice concepts, like you have now, are not entirely comfortable with them. It seems like a pretty good framework, but it still seems to leave a weird taste in the mouth. In my experience, the reason is often twofold. Take a look at the following statements:

The first statement is simply not true. There's an interesting discussion on this on Bob Lee's blog that describes why. I'll summarize it for you here. Let's start off with Kevin Bourrillion's words on annotations: "They do absolutely nothing to impede you from testing your code, or from using the classes with Spring."

Bob Lee goes on to add that you can easily create a separate Guice integration package for your application if you don't want a compile-time dependency on Guice. Though SpringSource's Colin Sampaleanu argued that you'd still need the Guice JAR on your classpath when migrating to another framework because you're using Guice annotations in your code, in reality, this is a nonissue. Here's why:

Note: At this point, people often argue that Guice should make it possible for users to configure their own annotation, instead of @Inject. This appears to be a good idea, but it wouldn't buy you anything. Even if you were using your own annotations, you would still have them in your code, because you want to use Guice. I don't see how that's any different to using Guice's @Inject. On the other hand, that feature would enable Guice to make use of EJB 3's @Resource annotations, for example. That's a valid use case, there will be a more general way to enable this kind flexibility in a future Guice release.

I think the conclusion is simple. Some people hate it when JavaDoc pollutes their code; others don't. The annotations debate is, in my opinion, a matter of taste and nothing more. There is no solid technical argument against the use of annotations.

Let's move on to the second statement, which asserts that the Spring Framework has done what Guice does for years. That's true, as long as you're talking about the DI idiom in general. But there are some differences that you should be aware of.

First, the Spring Framework still heavily depends on XML configuration. Your configuration, containing object wiring as well as other properties, is externalized by default when using the framework. In more recent versions of the framework, notably version 2.5, Spring has added support for annotation-driven object wiring. Unfortunately, you will probably still end up defining your beans in XML. You can get away with a single line of XML if you really want to, but that mode of operation requires you to put your bean configuration directly on the beans themselves, which is not as flexible as, say, using Guice modules. Alternatively, you can also use the JavaConfig option, but that feels like writing XML in Java. Anyway, my advice is to stay away from Spring's annotation-driven configuration options altogether. If you're going to use Spring, use the XML. It's the best documented option, and tools like Spring IDE are good enough to compensate for a lot of the annoyances.

With Guice your configuration will be done in Java by default, with externalized properties as an option. Externalized object wiring configuration is highly overrated and often not worth the added complexity and tooling dependencies. When was the last time you really changed your object wiring after deployment?

Note: I should mention that you can and probably should solve your Guice dynamic object wiring needs, if any, by loading modules dynamically and not by fully externalizing configuration in a custom file format or scripting language (like Ruby).

Second, because Guice uses Java as its primary configuration option, its modules also get some things for free:

As Bob Lee likes to put it, types are the natural currency of Java since version 5 of the platform.

Third, Guice is much smaller, is easier to learn and use, and has a much better power to complexity ratio than Spring. For an example of Guice's simplicity, I urge you to take a look at Guice AOP. The Spring Framework, however, definitely has its value, including full-featured integration with lots of open source and commercial products and various Java Enterprise Edition (Java EE) standards. I've used Spring in the past and will probably continue to use it. Guice is not the new Spring and doesn't try to be. In fact, there's no reason why both can't coexist.

Last but not least, let me emphasize that Guice is a DI framework, not a fullstack application framework like Spring. Comparing them in terms of feature set is like comparing apples and oranges. Use the frameworks that fit your needs.

Summary

Now that you have most of the basic building blocks, you're ready to stop eating fortune cookies and dive into the advanced functionality. Let me recap what you've seen so far. To use Guice, besides using a DI style of programming, you generally do the following:

You also learned that:

And remember: Every binding is represented by a Key, the whole Key, and nothing but the Key, so help me Bob.

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.