Rich Newman

May 28, 2010

A Comparison of Some Dependency Injection Frameworks: Part 7 Spring JavaConfig

Introduction

This series of articles is examining a number of dependency injection frameworks.  Part 3 of the series looked at the Spring framework configured with XML.  Part 5 and part 6 looked at Guice.  This article will apply the same tests to the Spring framework but configured in code using the JavaConfig download.

Code

The code for this article is available.

Note that JavaConfig used to be a separate download but is now being folded into core Spring (in Spring 3.0 and later versions).  However for this article I used the separate download version and Spring 2.2. 

JavaConfig

Spring JavaConfig is very similar to Spring XML except that all configuration is done in code rather than using XML.  The syntax for retrieving objects from a configured container is identical to Spring XML.

Specifically configuration is done by writing a class and annotating it with the @Configuration annotation.   We then configure individual objects by writing methods within the class.  We make the methods return the object we require, usually by just instantiating it with the ‘new’ keyword.  Then we annotate the method with the @Bean annotation:

@Configuration
public class ApplicationConfig {
      @Bean
      public Movie AndreiRublevMovie() {
            return new Movie("Andrei Rublev", "Andrei Tarkovsky");
      }

The effect of this is identical to configuring a bean in XML in Spring XML.  The method name in JavaConfig (AndreiRublevMovie) corresponds to the ID of the object in Spring XML.  As we shall see, the various techniques for configuring beans in Spring XML are also available in JavaConfig.

We configure our container with a different application context and a constructor that takes the name of the configuration class (or classes):

            JavaConfigApplicationContext context = new JavaConfigApplicationContext(ApplicationConfig.class);

We can now retrieve objects from the container using the getBean method and the method name as a (string) identifier as usual:

            Movie andreiRublev = (Movie) context.getBean("AndreiRublevMovie");

Testing Spring JavaConfig

JavaConfig uses exactly the same syntax for retrieving and using objects as does Spring XML.  As a result our StartUp class for JavaConfig is almost identical to the StartUp class for Spring XML.  The only difference is the application context creation described above.

So the only code differences between the tests for Spring XML and JavaConfig are in the way the container is configured.  As you can see this is very straightforward.

I'm not going to go through the configuration for all the tests as a result. 

Note that:

  • For tests 2 and 3 it's very easy to define different objects to be injected into the same class in different circumstances: you just instantiate two instances of the class in different methods and give them the dependent objects as shown below, where simpleMovieFinder and colonDelimitedMovieFinder are configuration methods that return the appropriate MovieFinder objects.
  • For tests 4 and 5, to specify singleton or prototype scope you just add the 'scope' annotation (the default is singleton).  This is also shown below.
      @Bean(scope=DefaultScopes.SINGLETON)
      public MovieLister simpleMovieLister() {
            return new MovieLister(simpleMovieFinder());
      }
      
      @Bean(scope=DefaultScopes.PROTOTYPE)
      public MovieLister colonDelimitedMovieLister() {
            return new MovieLister(colonDelimitedMovieFinder());
      }
  • For test 6, to use a factory method to create a class is again intuitive and simple.  You just create the factory in one configuration method and then call the factory method on it directly where you want your object returned in a second configuration method:
      @Bean(scope=DefaultScopes.PROTOTYPE)
      public ComplexMovieListerFactory complexMovieListerFactory() {
            return new ComplexMovieListerFactory();
      }
      
      @Bean(scope=DefaultScopes.PROTOTYPE)
      public MovieLister complexMovieLister() {
            return complexMovieListerFactory().build();
      }

Comments

JavaConfig does seem a step forward compared to the XML configuration problems we have previously been wrestling with for dependency injection.  In  particular it has the following advantages:

  • There is no XML, and the configuration in code means that there is some level of compile-type checking.  However, you can (and usually do) use string identifiers to retrieve objects, so there is still scope for typos that won't manifest themselves until runtime.   Note that it is possible to retrieve objects from the container by type to avoid this.
  • There is no need for code changes in classes (no annotations are applied to the classes you are injecting or injecting into)
  • The configuration class is very simple, and uses syntax familiar to any developer.  Unlike any other framework here you're not being compelled to learn something radically different to use JavaConfig.  All you have to learn is how the few attributes you need are used.
  • We're configuring in code using usual Java syntax.   This makes some of the constructs we have special syntax for in other frameworks seem very odd and cumbersome.  In particular the factory test above almost seems silly: we get our object by returning it from a method, obviously we can do that by just instantiating it OR by instantiating another class that will build it and return it from a build method.

JavaConfig is so simple that it makes you wonder why you need a framework at all.  Previously in this series of articles I've suggested that the tests may be a little more advanced than you'd actually use in practice.  But apart from tests 3 and 4 (singleton and prototype creation) none of the tests really need the Spring framework at all now we're using JavaConfig.  We could take all the attributes out and just use the ApplicationConfig class as a factory class.  Even tests 3 and 4 could easily be coded without the framework.  More on this later.

Conclusion

Code configuration as used in JavaConfig seems a simple and intuitive alternative to the more usual XML configuration.

Part 8 of this series will look at Microsoft's Unity framework.

May 22, 2010

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

Introduction

Part 5 of this series of articles started examining Guice as a dependency injection framework.  This article completes that, and makes some general comments on the Guice framework.

The code for this article is available (it’s the same code as in part 5).

Testing Guice (Continued)

Tests 4 and 5:  Creation of an object with singleton scope, creation of an object with prototype scope

As before we run tests 4 and 5 by making the MovieLister class from test 2 have singleton scope, and the MovieLister from test 3 have prototype scope.  We do this in the private module configuration classes set up in test 3.

The syntax for specifying that a class is a singleton is straightforward, and can be seen in the SimpleMovieListerPrivateModule class:

            bind(MovieLister.class)
            .annotatedWith(Simple.class)
            .to(MovieLister.class)
            .in(Singleton.class);

Note that we've just added the simple syntax '.in(Singleton.class)' to our binding to specify the singleton.

The default scope is prototype here, so in the ColonDelimitedMovieListerPrivate module class we don't need to do anything special.  So if we make the simple singleton change above the tests pass.

Test 6: Use of a factory class and method to create a dependent object.

Test 6 is intended to test our ability to use code (in a different) class to generate the object we require when we request it from the container.  Here we want to return a MovieLister from a factory class.

In Guice you can do this with a 'provider': the provider is the equivalent of the factory from earlier Spring examples.  For our test we write a class that implements the Provider<MovieLister> interface.  As you can see this interface has a 'get' method that returns a MovieLister, which is our factory method.

We need to bind this so that when we request a MovieLister our factory method gets called.  To do this we put the binding code below into our main module (configuration) class:

            bind(MovieLister.class)
            .annotatedWith(Complex.class)
            .toProvider(ComplexMovieListerFactory.class);

Note that we need an annotation because we already have other configurations that return type MovieLister from the container.  Now if we request an object from the container with the MovieLister/Complex key as below Guice will run the get method on our ComplexMovieListerFactory and return the result, which is what we want:

            MovieLister complexLister = guiceInjector.getInstance(Key.get(MovieLister.class, Complex.class));
            Movie[] tarkovskyMovies = complexLister.moviesDirectedBy("Andrei Tarkovsky");
            for(Movie movie : tarkovskyMovies){
                  System.out.println(movie.toString());

Whilst this isn't particularly complex it is quite a different approach to the configuration approaches we've already seen in Guice: now we're implementing an interface to get Guice to configure an object.

Test 7: Injection of the container itself (so it can be used to retrieve objects).

The code for this again is in the ComplexMovieListerFactory class.  Here Guice is actually simpler than Spring, since it doesn't require implementation of a specific interface to inject the container.  You simply use the usual syntax in your class: here we write a constructor with the appropriate signature to take the container, and mark that constructor with the @Inject attribute:

public class ComplexMovieListerFactory implements Provider<MovieLister> {
      
      private Injector injector;
      
      @Inject
      public ComplexMovieListerFactory(Injector injector) {
            this.injector = injector;
      }
...

The rest of the ComplexMovieListerFactory class is fairly self-explanatory.  One thing to note is that (as far as I can see) the Guice container has no equivalent of Spring's 'getBeansOfType' method, so we're having to retrieve the Movie objects individually from the container.  My feeling is this is not really important as this is a little unrealistic: in practice: we'd be unlikely to want to retrieve instances of all objects in the container of a given type.

Comments on Guice

Guice is directly trying to deal with some of the problems with XML configuration I outlined in the first article in this series.  In particular it's trying to remove those incomprehensible runtime errors you can get if  you your XML configuration is wrong.

For simple injection scenarios it does seem like a step forward.  However, I find Guice quite hard to like (and when I started this analysis I really wanted to like it).

  • There are several different ways of configuring an object.  As you can see from the length of this article and part 5, this can get quite confusing and difficult.  You have to learn and understand the different scenarios to be able to use the framework.
  • The different configuration methods are used in different circumstances depending on the complexity of the configuration scenario.  As we have seen, this can include changing the configuration for an existing object when a new object of the same type needs to be configured.  Again you have to understand and be able to handle this. 
  • For any moderately complex injection scenario you are having to change the code of your classes by annotating them.  This effectively means we have construction logic back in our code classes, albeit in a limited way and in the form of metadata rather than actual code.
  • The code in the configuration classes isn't particularly intuitive.  In particular the use of private modules for the solution to the so-called 'robot legs' problem leads to quite complex code.

Overall my feeling is that Guice does address some of the issues with XML configuration that we see in frameworks like Spring.  However it then introduces a number of issues of its own.  Obviously I have tested with some reasonably advanced scenarios here.  Some of these may not need to be used that frequently in real production code. I haven't personally used Guice on any real life project.  But on the basis of this analysis I think I would be reluctant to attempt to do so.

Conclusion

Guice was the most disappointing framework I looked at.  Its design goals are explicitly to solve some of the problems with XML configuration discussed in part 1 of this series of articles.  However, it seems to me it does this in a way that only works well in very simple dependency injection scenarios.  As soon as there is a moderate level of complexity the developer is having to learn several different configuration techniques, some of which aren't easy to use, and how to apply them.

Part 7 will look at Spring JavaConfig.

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.

May 15, 2010

Table of Contents: A Comparison of Some Dependency Injection Frameworks

Part 1 Introduction and Motivation

A discussion of the motivation behind looking at a number of dependency injection frameworks, and which ones we will look at.

Part 2 Scenarios To Be Tested

A description of the various tests to be run on the dependency injection frameworks.

Part 3 Spring with XML Configuration (Java)

A detailed look at running the tests in Java Spring using XML configuration, with some discussion on the merits of this framework.

Part 4 Spring.Net with XML Configuration (.Net)

A very brief look at running the tests in Spring.Net, which is almost identical to running the tests in Spring for Java with XML configuration.

Part 5 Guice (Java)

A brief discussion of how Guice works, and a look at the first three tests implemented in Guice.

Part 6 Guice (Java) Continued

A look at the remainder of the tests implemented in Guice, and some comments on whether the framework solves the problems with other dependency injection frameworks.

Part 7 Spring JavaConfig (Java)

Examines how we would run our tests using Spring JavaConfig, and discusses why it seems a step forward from the usual XML configuration in Spring and other frameworks.

May 10, 2010

A Comparison of Some Dependency Injection Frameworks: Part 3 Spring with XML Configuration (Java)

Introduction

Part  2 of this series of articles discussed some tests we will be applying to a variety of dependency injection frameworks.  It also showed the classes that didn’t substantially change regardless of the framework.

This article will look at how we run these tests in the Spring framework.

Code

The full code for the examples in this article is available.  This download is an Eclipse project, and contains all required dependencies.  All you should need to do is to import the the downloaded zip file into Eclipse and the code should run successfully.  To do this I’m using Eclipse Galileo, which is the latest version at the time of writing.  Here the menu options are File/Import…/ Existing Projects into Workspace (under General)/select the downloaded zip file.

Testing Spring with XML Configuration

The XML configuration for our Spring project is straightforward, as is the associated client startup class that uses it.  In the sections below we look in a little more detail at how we do the configuration and retrieve and use our objects using Spring.

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

As discussed in part 2, we use the Movie class to run this test.  The XML configuration for this is below: we tell spring we need a bean with an id of ‘AndreiRublevMovie’ instantiated from class Movie, and we pass in string values for the two constructor arguments:

      <bean id="AndreiRublevMovie" class="moviefindertest.springxml.Movie">
            <constructor-arg name="title" value="Andrei Rublev"></constructor-arg>
            <constructor-arg name="director" value="Andrei Tarkovsky"></constructor-arg>
      </bean>

We can then retrieve an object configured as above from our container with the code below:

            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            Movie andreiRublev = (Movie) context.getBean("AndreiRublevMovie"); 

Obviously this isn't a very useful test on its own, but it does show that in Spring configuring an object with constructor injection is fairly easy and intuitive.

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

Similarly our second test is to inject a (singleton) instance of SimpleMovieFinder into an instance of our MovieLister class using constructor injection.  The configuration for this is as below:

      <bean id="simpleMovieFinder" class="moviefindertest.springxml.SimpleMovieFinder"></bean>
      <bean id="simpleMovieLister" class="moviefindertest.springxml.MovieLister" scope="singleton">
            <constructor-arg name="finder" ref="simpleMovieFinder"></constructor-arg>
      </bean>

SimpleMovieFinder gives MovieLister a hard-coded list of movies, including several James Cameron movies.  In the startup class we  retrieve the instance of MovieLister from the container as you would expect, and then use the moviesDirectedBy method to retrieve the James Cameron movies:

            MovieLister simpleLister = (MovieLister) context.getBean("simpleMovieLister");
            Movie[] jamesCameronMovies = simpleLister.moviesDirectedBy("James Cameron");
            for(Movie movie : jamesCameronMovies){
                  System.out.println(movie.toString());
            }

This is a more useful test than 1. above since we're actually configuring our object.  Again the syntax for injecting one object into another is simple with Spring, and again retrieving the object and using it is easy.

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

The third test is to inject a different object (an instance of ColonDelimitedMovieLister)  into an instance of the same class as in the test above (MovieLister). 

      <bean id="colonDelimitedMovieFinder" class="moviefindertest.springxml.ColonDelimitedMovieFinder"></bean>
      <bean id="colonDelimitedMovieLister" class="moviefindertest.springxml.MovieLister" scope="prototype">
            <property name="finder" ref="colonDelimitedMovieFinder"></property>
      </bean>

            MovieLister colonDelimitedLister = (MovieLister) context.getBean("colonDelimitedMovieLister");
            Movie[] martinScorseseMovies = colonDelimitedLister.moviesDirectedBy("Martin Scorsese");
            for(Movie movie : martinScorseseMovies){
                  System.out.println(movie.toString());
            } 

As we shall see, in several other DI frameworks doing this isn't particularly easy.  Here the XML and code are almost identical to the code for the second test above.  Note that it's a different list of movies (retrieved from a text file), and a different director.  Note also that we configure the MovieLister with scope="prototype":

Tests 4 and 5:  Creation of an object with singleton scope, creation of an object with prototype scope

For these tests we re-use the configuration in tests 3 and 4 above.  In test 3 simpleMovieLister was configured with singleton scope.  This means if we use getBean to retieve simpleMovieLister a second time it should return the same object.  In test 4 colonDelimitedMovieLister was configured with prototype scope.  This means a second request should return a new object.

Some simple version numbers in the MovieLister class itself let us identify whether we have the same or a different object when the tests are run.

Tests 6 and 7:  Factory Method and Injection of the Container

Part 2 of this series of articles showed the code that was invariant across all of the frameworks tested here, and briefly discussed our final two tests.  These involve:

  • Creating a MovieFinder class using a factory class and method.
  • Injecting the container itself into that factory class so that all objects of type Movie could be retrieved from it.

The factory class itself inevitably varies depending on which framework you are using.  The code for this factory class with Spring is available.  In Spring we implement the BeanFactoryAware interface, which has a setBeanFactory method, to inject the container into a class.  The 'bean factory' is the container, and as shown this has methods like getBeansOfType which allow us to get all objects of a certain type configured in the container.  This is pretty straightforward.

Now we can configure our MovieLister, which is simply returned from the 'build' method on the factory class.  Setting this up in Spring XML is again straightforward: we simply set the factory-bean attribute of our bean configuration to be the id of an instance of the factory class, and the 'factory-method' attribute to be the name of the method:  

      <bean id="complexMovieListerBuilder" class="moviefindertest.springxml.ComplexMovieListerFactory"></bean>
      <bean id="complexMovieLister" factory-bean="complexMovieListerBuilder" factory-method="build" scope="prototype"></bean>

 Now we can use the bean in exactly the same way as in our other tests: it doesn't matter that the bean configuration is calling a method to construct the object, rather than just instantiating a class (as in our other tests):

            MovieLister complexLister = (MovieLister) context.getBean("complexMovieLister");
            Movie[] tarkovskyMovies = complexLister.moviesDirectedBy("Andrei Tarkovsky");
            for(Movie movie : tarkovskyMovies){
                  System.out.println(movie.toString());

Comments on Spring

Spring has, of course, been a very successful framework, and part of that is the simplicity of the dependency injection configuration.  It's easy to set up the XML configuration, and it's fairly easy to understand what it's doing by just looking at it.  The syntax is certainly less obscure than some of the other frameworks we will be looking at in this series of articles.  It's not Java, of course, so  the developer is still having to learn a new way of instantiating their objects from what they were taught in Java 1.01. 

Spring configuration is almost totally non-intrusive.  That is, if you are using dependency injection to inject an instance of one class into an instance of another class then you don't have to change the code of either class at all.  All the work can be done in the XML and the configuration of the container.

Spring is also quiet powerful.  It can run all of our tests successfully.   Having said that, the tests are pretty arbitrary.  The framework I'm most familiar with is Spring, so that tests may be tailored to what Spring can do, particularly tests 6 and 7.

So Spring is, as we know, a pretty good dependency injection framework.  However, as I discussed in part 1, XML configuration of the kind we use here has a number of drawbacks.  The most important of these to me is the fact that it can be difficult to maintain and debug XML configuration, particularly on large projects.

Later articles in this series will look at whether there are alternatives to Spring that address these drawbacks.

Conclusion

This article briefly examined using XML configuration in Spring for dependency injection in Java.  Part 4 of the series will even more briefly look at running the same tests in Spring.Net.

May 1, 2010

A Comparison of Some Dependency Injection Frameworks: Part 2 Scenarios To Be Tested

Filed under: .net, Dependency Injection, DI Frameworks, Java — Tags: , , — richnewman @ 11:24 pm

Introduction

Part one of this series of articles discussed a set of dependency injection frameworks that we will be examining, and also the motivation behind this.

This article describes the tests we will be performing and how they will be constructed in code.

The Scenarios Tested

The following dependency injection scenarios will be tested:

  1. Configuration and retrieval of an object with (only) string dependencies. 
  2. Injection of an object into an instance of a class.
  3. Injection of a different object into an instance of the same class as used in test 2.
  4. Creation of an object with singleton scope (same object retrieved from the container every time requested).
  5. Creation of an object with prototype or transient scope (different (new) object retrieved from the container every time requested).
  6. Use of a factory class and method to create a dependent object.
  7. Injection of the container itself (so it can be used to retrieve objects).

This seems like a reasonable selection of basic and more advanced DI scenarios to me.

I have decided not to look at AOP (aspect-oriented programming) in these frameworks, or other features such as event brokers; I want to purely focus on dependency injection.  I also won’t test how hierarchies of containers might work.

The Example

Having decided to test the dependency injection scenarios described above I needed a simple example to base the tests on.  The logical place to start seemed to be MovieLister from the original Martin Fowler article on dependency injection.  Whilst this is not a realistic example most developers are familiar with it, and the scenarios I’m testing are not particularly realistic (in isolation) either.  As mentioned, most people are familiar with MovieLister, but I briefly recap it below.

Summary of MovieLister

The idea is that you have one generic MovieLister class that has one key method with signature

public Movie[] moviesDirectedBy(String director)

This will return all movies directed by the passed-in director.  The universe of all movies that these are selected from is provided by another class, which implements the MovieFinder interface.  This universe of movies can be different under different circumstances, so an instance of the MovieFinder is dependency injected into MovieLister.  The MovieFinder interface has one method as below:

List<Movie> findAll();

The MovieLister will call this method to get the list of all movies, and then filter for the ones directed by the passed-in director.

As mentioned above, more extensive details on this example can be found in Martin Fowler’s article.

Applying MovieLister to the Test Scenarios

Clearly MovieLister in its basic form does not test all the scenarios that we want to test as outlined above.

How MovieLister will be used for these scenarios is described below.  The links are to listings of the actual code for the relevant classes in both Java and .Net.  Working code will be posted in future articles in this series.

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

Obviously there is a Movie class underlying MovieLister (.Net version).  For the purposes of these tests this just has title and director string properties, which can be set by setters or by a constructor, and a ToString override that outputs the title and director of the current movie.

The first of our tests simply uses the Movie class.  We configure an instance that has a specific title and director using constructor injection, and then retrieve that from our DI container and output the result of calling ToString on it.

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

We create a SimpleMovieFinder class that returns a hard-coded list of movies from the findAll method (.Net version).  We then inject that into a MovieLister class (.Net version), and call the moviesDirectedBy method from the MovieFinder interface ( .Net version).  This retrieves a list of movies that we iterate over and display with ToString.

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

We create a ColonDelimitedMovieFinder class (.Net version).  This loads the contents of a colon-delimited file, Movies.txt, and returns the movies in that in its findAll method.  We inject an instance of this into an instance of the same MovieLister class as in 1., and output the results in the same way.

Surprisingly, many of the frameworks struggled with this test, although it seems pretty basic to me.  In fact the Guice team have given a name to the ‘problem’ of injecting different objects into a class in different circumstances: the ‘robot legs problem’.

4.  Creation of an object with singleton scope (same object retrieved from the container every time requested).

Rather than create a new object for this we simply make the MovieLister from test 2 into a singleton.  Then for the test we request it from the DI container a second time.  We need some way of identifying whether it’s the same object, and to do this we assign every instance of MovieLister an ID via a static method in the class.  In this test the IDs of the two objects should be the same (since they are the same object).

5.  Creation of an object with prototype or transient scope (different (new) object retrieved from the container every time requested).

Again we reuse code from an earlier test.  In this case we make the MovieLister from test 3 have transient scope, and to test it we request it from the DI container a second time.  We then look at the version IDs of the two objects as above: in this case they should be different.

6.  Use of a factory class and method to create a dependent object.

For this we set up a ComplexMovieFinder class (.Net version).  As you can see this is a very simple class that just returns a list of movies it has been given from its findAll method.  The complexity comes in how the list of movies is constructed and handed to this class.  This class will be instantiated, and have its list of movies handed to it, by the ComplexMovieListerFactory class.  This class varies in implementation depending on the framework used: see section 7 below.

7.  Injection of the container itself (so it can be used to retrieve objects).

We combine this with test 6 above:  the ComplexMovieListerFactory class will have the container injected into it and will retrieve all objects of type Movie from it.  It will then create the ComplexMovieFinder class and give it this list of movies.

Unlike the other classes shown in this article, the code for the ComplexMovieListerFactory varies greatly depending on the DI framework.  In fact this test cannot be performed in all frameworks.

We will look at the code for it in the Spring Java framework in the next article in this series.

Conclusion

This article outlined the tests we will be performing on our various DI frameworks.  Part 3 of this series of articles shows these tests implemented in the Spring Java framework.  This includes the code for the factory class described immediately above, the XML configuration for the container, and the code to execute the tests.

The Shocking Blue Green Theme. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 80 other followers