Rich Newman

May 22, 2010

A Comparison of Some Dependency Injection Frameworks: Part 5 Guice (Java)

Introduction

This series of articles compares a number of dependency injection frameworks. Part 4 looked at Spring.Net.  This article and part 6 look at Google’s Guice framework.

Guice

Guice is a valiant attempt by those clever guys at Google to make dependency injection simpler, and to remove some of the problems with XML configuration described in part one of this series.  In the Guice framework:

  • All configuration is done in code.  There is no undebuggable XML.  Configuration is typically done in standard Java classes that extend Guice’s AbstractModule base class.
  • Where only one configuration of a class is needed in a program the sole instance can be injected by type.  (This is like autowiring in Spring). 
  • If more than one configuration of a class is needed Java annotations (plus the type) are used to identify the different configurations.  The annotations are applied in the code at the point where the object is injected.  We’ll see  how this works in a moment, but it means we no longer need string identifiers for objects.
  • Where the annotation approach is not powerful enough other techniques may need to be used.  As we shall see these techniques include using providers, and using private modules.

All of this allows our compiler to check for most configuration problems at compile time.  In particular runtime errors from string identifiers being misspelt, or the wrong type being injected, should disappear.

This seems promising.  Let’s see how we can handle our tests.

Code

The full code for this example is available.  As you can see, it ends up having significantly more code files than the code for the other frameworks examined in this series of articles.  In particular there are a number of annotation interfaces (Simple, ColonDelimited, Complex and AndreiRublev).  Most of our basic classes for the example remain the same (Movie, SimpleMovieFinder, ColonDelimitedMovieFinder, ComplexMovieFinder, MovieLister etc.).  However, the MovieLister class now has an ‘@Inject’ annotation applied to the constructor that is used to inject the MovieFinder. 

Testing Guice

We again go through our tests in some detail as this illustrates how Guice works.

Test 1: Configuration and retrieval of an object with (only) string dependencies 

As mentioned above, in Guice we can configure an object by inheriting from Guice’s AbstractModule class, and overriding the configure method.  We can configure our single object with string dependencies either by type alone, or by type plus an annotation.

(i) Configuration by type alone

Our configuration class (‘module’) is set up as below:

public class MovieListerModule extends AbstractModule {

      @Override
      protected void configure() {
            bind(Movie.class)
            .toInstance(new Movie("The Sacrifice", "Andrei Tarkovsky"));

This says that if the code just requests an object of type Movie (without annotating the request) then a Movie Object with name ‘The Sacrifice’ will be returned.

We can request instances of these objects directly from the container using the syntax below.  Firstly we need to configure the container and retrieve an instance of it (as we do with other frameworks).  Here we are configuring the container with the configuration ‘module’ classes we need.  MovieListerModule is shown above, the other modules we shall see later:

            Injector guiceInjector = Guice.createInjector(new MovieListerModule(), new SimpleMovieListerPrivateModule(), new ColonDelimitedMovieListerPrivateModule());

Then we request our object by type alone:

            Movie andreiRublevMovie2 = guiceInjector.getInstance(Movie.class);
            System.out.println(andreiRublevMovie2.toString());

(ii) Configuration by type plus an annotation

The syntax above begs the question of how we configure a different Movie object to be injected elsewhere in our code.  The ‘binding’ to the Movie type alone is already used.  The answer is that we can create a key for our configuration based on the type (Movie) plus an annotation.  We can then configure an object and associate the configuration with the key, and retrieve the object from the container using the key.

To configure an object with a two-part key of this kind we put the code below in our MovieListerModule configuration class.  Here ‘AndreiRublev’ is an annotation defined elsewhere in our code:

            bind(Movie.class)
            .annotatedWith(AndreiRublev.class)
            .toInstance(new Movie("Andrei Rublev", "Andrei Tarkovsky"));

We can then request this object from the container using the key:

            Movie andreiRublevMovie = guiceInjector.getInstance(Key.get(Movie.class, AndreiRublev.class));
            System.out.println(andreiRublevMovie.toString());

Here we use Guice’s Key.get method to retrieve the key.  We then use that key with the getInstance method to retrieve the Andrei Rublev movie object from the container using the configuration shown above.

Note that there are no string identifiers here.  Note also that the getInstance method returns an object with the correct type Movie (because this is the first type passed in as an argument to the key).  This makes type errors much less likely than the equivalent XML approach.

Test 2:  Injection of an object into an instance of a class.

Our example here is to inject an instance of SimpleMovieFinder into class MovieLister.  Note that MovieLister expects an object of type MovieFinder (an interface) to be injected by constructor injection.

Note that the code shown in this section is not in the code sample.  The reasons for this are explained below.

Let’s look at how we’d do that using an annotation to try to distinguish different instances of MovieFinder being injected in different places.  To do this we need to configure MovieFinder with an annotation in our configuration class, as described above:

            bind(MovieFinder.class)
            .annotatedWith(Simple.class)
            .to(SimpleMovieFinder.class);

Now in our MovieLister class itself we can annotate the constructor as below:

public class MovieLister {
      
      private MovieFinder finder;
      
      @Inject
    public MovieLister(@Simple MovieFinder finder) {
            this.finder = finder;
      }

Here the @Inject annotation tells Guice that it should use this constructor to build this object for the container.  The @Simple annotation, combined with the type MovieFinder, tells Guice to use the binding above.  That is, it tells it that it should instantiate SimpleMovieFinder and inject it here.

We can now configure our container as above, and retrieve the SimpleMovieFinder object from it, and use it, using the usual syntax:

            MovieLister simpleLister = guiceInjector.getInstance(MovieLister.class);
            Movie[] jamesCameronMovies = simpleLister.moviesDirectedBy("James Cameron");
            for(Movie movie : jamesCameronMovies){
                  System.out.println(movie.toString());

This is again fairly straightforward.  However, note that we have had to alter the code file for the SimpleMovieFinder class to get it to work, admittedly only by adding annotations.

However, there is a problem here.  Our next test (test 3) is to inject a different object into MovieLister under different circumstances.  The annotation approach shown here clearly doesn’t allow this (we can’t have different annotations in the class in different circumstances).  We have to change our approach (and the code) when we decide we want to use this slightly more complex scenario.  This is the reason the code in the code sample is not as shown above: it has to take account of test 3.

Test 3:  Injection of a different object into an instance of the same class as used in test 2.

The Guice team have given this scenario a name: the ‘robot legs problem’.  We need a different technique to the standard Guice annotations to deal with it.  The problem and the solution are both described in the ‘How do I build two similar but slightly different trees of objects?’ section of Guice’s FAQ.

The solution uses private modules (configuration classes). 

In short we remove the @Simple annotation in the constructor of MovieLister shown under test 2 above.  Then we have separate configuration classes for the MovieLister that uses SimpleMovieFinder (SimpleMovieListerPrivateModule), and for the MovieLister that uses ColonDelimitedMovieFinder (ColonDelimitedMovieListerPrivateModule). 

As you can see, the two private module classes are very similar.  In both cases we bind the MovieFinder type to the class type we are interested in (SimpleMovieFinder or ColonDelimitedMovieFinder).  Since the modules are private we can now inject these by type alone.  We then bind the MovieLister class itself.  We DO need to annotate this binding, because in the client startup code we’ll need to be able to retrieve the two different MovieListers.  Finally we need to ‘expose’ the MovieLister so that Guice knows it is to be used by client code (rather than being private to the module).

We’ve already seen above how we tell the container to configure itself using all of these module files:

            Injector guiceInjector = Guice.createInjector(new MovieListerModule(), new SimpleMovieListerPrivateModule(), new ColonDelimitedMovieListerPrivateModule());

Finally we can use the two configurations in the same client code as below.  Note that we are retrieving the two different MovieListers by type plus annotation:

            MovieLister simpleLister = guiceInjector.getInstance(Key.get(MovieLister.class, Simple.class));
            Movie[] jamesCameronMovies = simpleLister.moviesDirectedBy("James Cameron");
            for(Movie movie : jamesCameronMovies){
                  System.out.println(movie.toString());
            }
            MovieLister colonDelimitedLister = guiceInjector.getInstance(Key.get(MovieLister.class, ColonDelimited.class));
            Movie[] martinScorseseMovies = colonDelimitedLister.moviesDirectedBy("Martin Scorsese");
            for(Movie movie : martinScorseseMovies){
                  System.out.println(movie.toString());
            }

This is a bit complicated compared to the rather simpler configuration in Spring.

Conclusion

We will look at the remainder of our test cases, and make some general comments on the Guice framework, in part 6.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: