Practical introduction to dependency injection

In the previous post we checked whether the template method pattern is an alternative to the dependency injection. There we saw the simplest dependency injection solution, without any framework.

Just to recap, this is the fizzbuzz class:

public class FizzBuzzManualInjection {

    private final FizzBuzzNumberProvider numberProvider;
    private final FizzBuzzTextProvider textProvider;
    private final FizzBuzzPrinter printer;

    public FizzBuzzManualInjection(final FizzBuzzNumberProvider numberProvider,
        final FizzBuzzTextProvider textProvider,
        final FizzBuzzPrinter printer) {
        this.numberProvider = numberProvider;
        this.textProvider = textProvider;
        this.printer = printer;
    }

    public void doFizzBuzz() {
        numberProvider.getFizzBuzzNumbers().forEach(num -> {
            final String msg = textProvider.getFizzBuzzText(num);
            printer.printFizzBuzz(msg);
        });
    }

}

And this is how we can configure it:

public class Main {

    public static void main(final String[] args) {
        final FizzBuzzNumberProvider numberProvider = new FizzBuzzNumberProvider();
        final FizzBuzzTextProvider textProvider = new FizzBuzzTextProvider();
        final FizzBuzzPrinter printer = new FizzBuzzPrinter();
        final FizzBuzzManualInjection fizzBuzz = new FizzBuzzManualInjection(numberProvider, textProvider, printer);
        fizzBuzz.doFizzBuzz();
    }

}

Motivation

Consider this graph:

One object creates another

One object creates another

Node a represents a object that creates two other objects, b and c. They create other objects to the bottom of the graph.

Suppose you want to inject a new collaborator to node j. Well, then you have to go through the path a-c-g-j and modify all the constructors.

Is this something you’re likely to do when the deadlines are close?

Then consider a flat tree hierarchy, like this one:

flat-creation

There is a main node that instantiates a framework. The, the framework creates every other object. If they need references to each other, then framework will inject them.

So you want to add something to node j? Well, all you need to do is to change the configuration of your framework, tweak node j a little, and you’re done.

Again:
Is this something you’re likely to do when the deadlines are close? I guess not. So let’s take a closer look at a few frameworks.

There is a third option: you skip the ‘framework’ node. This works well for smaller projects. However, for hundreds of different beans, it’s just unmanageable. So let’s see the frameworks!

Spring

Spring is the most obvious IoC container for java, so we’ll start with that. For this one I chose setter injection. I.e. the bean receives its dependencies via setters:


public class FizzBuzzService {

    private FizzBuzzNumberProvider numberProvider;

    private FizzBuzzTextProvider textProvider;

    private FizzBuzzPrinter printer;

    public void doFizzBuzz() {
        numberProvider.getFizzBuzzNumbers().forEach(num ->
        {
            final String msg = textProvider.getFizzBuzzText(num);
            printer.printFizzBuzz(msg);
        });
    }

    public void setNumberProvider(final FizzBuzzNumberProvider numberProvider) {
        this.numberProvider = numberProvider;
    }

    public void setTextProvider(final FizzBuzzTextProvider textProvider) {
        this.textProvider = textProvider;
    }

    public void setPrinter(final FizzBuzzPrinter printer) {
        this.printer = printer;
    }

}

As you can see, this class has a default constructor (which we don’t have to declare), three setters, and a business method. In order to make it work, we need to configure this bean with xml:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
 <bean id="fizzbuzz.printer" class="com.trev.fizzbuzz.inject.spring.DefaultFizzBuzzPrinter" />
 <bean id="fizzbuzz.numberProvider" class="com.trev.fizzbuzz.inject.spring.DefaultNumberProvider" />
 <bean id="fizzbuzz.textProvider" class="com.trev.fizzbuzz.inject.spring.DefaultTextProvider" />
 
 <bean id="fizzBuzz" class="com.trev.fizzbuzz.inject.spring.FizzBuzzService" >
 <property name="numberProvider" ref="fizzbuzz.numberProvider" />
 <property name="textProvider" ref="fizzbuzz.textProvider" />
 <property name="printer" ref="fizzbuzz.printer" />
 </bean>
 
</beans>

It’s quite easy to inject Spy implementations for testing. Here I use Mockito to do the spying for me:

@ContextConfiguration(locations = {"classpath:fizzbuzz.spring.xml"})
public class FizzBuzzIntegrationTest extends AbstractTestNGSpringContextTests {

    @Mock
    private FizzBuzzPrinter printer;

    @Mock
    private FizzBuzzTextProvider textProvider;

    @Mock
    private FizzBuzzNumberProvider numberProvider;

    @Autowired
    @Qualifier("fizzBuzz")
    private FizzBuzzService sut;

    @BeforeMethod
    void setUp() {
        initMocks(this);
        sut.setNumberProvider(numberProvider);
        sut.setPrinter(printer);
        sut.setTextProvider(textProvider);
    }

    @Test
    public void testFizzBuzz() {
        // GIVEN
        when(numberProvider.getFizzBuzzNumbers()).thenReturn(IntStream.of(1, 2, 3).boxed());
        when(textProvider.getFizzBuzzText(eq(1))).thenReturn("TEST-1");
        when(textProvider.getFizzBuzzText(eq(2))).thenReturn("TEST-2");
        when(textProvider.getFizzBuzzText(eq(3))).thenReturn("TEST-3");

        // WHEN
        sut.doFizzBuzz();

        // THEN
        verify(numberProvider).getFizzBuzzNumbers();

        verify(textProvider).getFizzBuzzText(1);
        verify(printer).printFizzBuzz("TEST-1");

        verify(textProvider).getFizzBuzzText(2);
        verify(printer).printFizzBuzz("TEST-2");

        verify(textProvider).getFizzBuzzText(3);
        verify(printer).printFizzBuzz("TEST-3");
    }
}

In @BeforeMethod we simply set the mocks. Then, for individual test setups we configure the mocks. Finally we can call verify on the mocks just to make sure that the right print() methods were called.

Spring java config

There’s a recent an improvement for Spring IoC: you can configure the classes in POJO-s too. This is how that looks like:

@Configuration
public class FizzBuzzConfig {

    @Bean
    public FizzBuzzPrinter fizzBuzzPrinter() {
        return new DefaultFizzBuzzPrinter();
    }

    @Bean
    public FizzBuzzNumberProvider numberProvider() {
        return new DefaultNumberProvider();
    }

    @Bean
    public FizzBuzzTextProvider textProvider() {
        return new DefaultTextProvider();
    }

    @Bean
    public FizzBuzzService fizzBuzz() {
        return new FizzBuzzService();
    }

}

Remember, we have this code instead of the big xml. Everything else is just the same.

Let’s see how we can bootstrap our spring contexts. This is how we do for the xml configuration:

public class Main {
    public static void main(final String[] args) {
        try (final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("fizzbuzz.spring.xml")) {
            final FizzBuzzService fizzBuzzService = context.getBean(FizzBuzzService.class);
            fizzBuzzService.doFizzBuzz();
        }
    }
}

And this is how we do with java config:

public class Main {
    public static void main(final String[] args) {
        try (final AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(FizzBuzzConfig.class)) {
            final FizzBuzzService fizzBuzz = context.getBean(FizzBuzzService.class);
            fizzBuzz.doFizzBuzz();
        }
    }
}

All the difference we have is about instatiating the application context. One refers to an xml resource while the other one refers to a class. That’s it.

Guice

Guice is a lightweight dependency injcetion framework from Google. It provides java config only. This code demonstrates how FizzBuzz looks like with guice annotations:

public class FizzBuzzGuiceService {

    private final FizzBuzzNumberProvider numberProvider;
    private final FizzBuzzTextProvider textProvider;
    private final FizzBuzzPrinter printer;

    /**
     * A constructor that needs module configuration.
     *
     * @param numberProvider creates a Stream of Integer
     * @param textProvider translates integers to text
     * @param printer prints the texts
     */
    @Inject
    public FizzBuzzGuiceService(final FizzBuzzNumberProvider numberProvider,
        final FizzBuzzTextProvider textProvider,
        final FizzBuzzPrinter printer) {
        this.numberProvider = numberProvider;
        this.textProvider = textProvider;
        this.printer = printer;
    }

    public void doFizzBuzz() {
        numberProvider.getFizzBuzzNumbers().forEach(num -> {
            final String msg = textProvider.getFizzBuzzText(num);
            printer.printFizzBuzz(msg);
        });
    }

}

What do we have here?

There’s a constructor that accepts interfaces of the required dependencies. Then there’s this very abstract doFizzBuzz() method, and that’s it.

If we want to call this from command line, we need a guice module configuration and a main method that bootstraps it. This is how a guice configuration looks like:

public class FizzBuzzModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(FizzBuzzNumberProvider.class).to(DefaultNumberProvider.class);
        bind(FizzBuzzPrinter.class).to(DefaultFizzBuzzPrinter.class);
        bind(FizzBuzzTextProvider.class).to(DefaultTextProvider.class);
    }

}

The module has to extend AbstractModule, but that’s it. The rest of the system is based on plain old java objects. Then, this is how you bootstrap it from main:

public class Main {

    public static void main(final String[] args)
    {
        final Injector injector = Guice.createInjector(new FizzBuzzModule());

        final FizzBuzzGuiceService fizzBuzz = injector.getInstance(FizzBuzzGuiceService.class);

        fizzBuzz.doFizzBuzz();

    }

}

Guice looks pretty much like Spring java config. Instead of context we have an Injector. But everything else is the same: the injector creates objects for you recursively.

Guice default configuration

There’s one more trick with Guice: if you need only one implementation, then you can skip the module configuration. Suppose this is your Guice service:

public class FizzBuzzGuiceService {

    private final FizzBuzzNumberProvider numberProvider;
    private final FizzBuzzTextProvider textProvider;
    private final FizzBuzzPrinter printer;

    /**
     * This one doesn't need module configuration
     */

    @Inject
    public FizzBuzzGuiceService(final DefaultNumberProvider numberProvider,
        final DefaultTextProvider textProvider,
        final DefaultFizzBuzzPrinter printer) {
        this.numberProvider = numberProvider;
        this.textProvider = textProvider;
        this.printer = printer;
    }

    public void doFizzBuzz() {
        // ...
    }

}

Then this is how you can bootsrap it from main:

public class Main {

    public static void main(final String[] args) {
        final Injector injector = Guice.createInjector();

        final FizzBuzzGuiceService fizzBuzz = injector.getInstance(FizzBuzzGuiceService.class);

        fizzBuzz.doFizzBuzz();

    }

}

The only difference is that you don’t need to provide a module configuration for the Guice.createInjector() method. Otherwise it’s pretty much the same.

Testing and default injector

You can use the default injector, as shown above. It’ll add a constraint for testing: you must use mocks or subclasses for test doubles. As long as you don’t need the extra flexibility that interfaces provide, you’ll be fine.

Tapestry

The Tapestry framework has an IoC module too. In an earlier post we delved into its details. For now let’s see how we can use it for our FizzBuzz application.

One big difference between Spring and Tapestry is that Tapestry calls the injected things Service-s while Spring calls them beans. Another one is that Tapestry uses field-injection. I.e. you don’t need to write any setters or constructors for your dependencies:

public class FizzBuzzTapestryService {

    @Inject
    private FizzBuzzNumberProvider numberProvider;

    @Inject
    private FizzBuzzTextProvider textProvider;

    @Inject
    private FizzBuzzPrinter printer;

    public void doFizzBuzz() {
        for (final Integer num : numberProvider.getFizzBuzzNumbers()) {
            final String msg = textProvider.getFizzBuzzText(num);
            printer.printFizzBuzz(msg);
        }
    }
}

Three @Inject-s, that’s it. The configuration is a little more interesting. For Tapestry you need to write Module classes where you can define the bindings. You can actually do way more than that, like you can run any initialization code that you might need. But for now all we need is bindings:

public class FizzBuzzModule {

    public static void bind(final ServiceBinder binder) {
        binder.bind(FizzBuzzNumberProvider.class, DefaultNumberProvider.class);
        binder.bind(FizzBuzzPrinter.class, DefaultFizzBuzzPrinter.class);
        binder.bind(FizzBuzzTextProvider.class, DefaultTextProvider.class);
        binder.bind(FizzBuzzTapestryService.class); // concrete service class
    }

}

Just for the sake of consistency, you can bootstrap it from main like this:

public class Main {

    public static void main(final String[] args) {
        // code inspired by http://wiki.apache.org/tapestry/Tapestry5HowToIocOnly
        final RegistryBuilder builder = new RegistryBuilder();
        builder.add(FizzBuzzModule.class);

        final Registry registry = builder.build();
        registry.performRegistryStartup();

        final FizzBuzzTapestryService fizzBuzz = registry.getService(FizzBuzzTapestryService.class);
        fizzBuzz.doFizzBuzz();
    }

}

It’s a bit more tedious than necessary. But hey, you need to do it only once in your application. Actually, for Tapestry webapps the framework does it for you.

Odds and ends

This post focuses on dependency injection. But these frameworks do more than that. E.g. they provide aspects and defer object creation.

There are lots of other DI frameworks I didn’t mention here. This post compares a few others.

Did I miss your favorite framework? Did I miss an important feature? Please let me know in comments.

Advertisements

About tamasrev

A software developer, specialized in Java, addressing himself as generalist. A proud daddy.
This entry was posted in Uncategorized. Bookmark the permalink.

4 Responses to Practical introduction to dependency injection

  1. Kofa says:

    Tamás, Java Config was introduced with Spring 3.0, in 2009, so it’s not exactly a recent improvement. 🙂
    Plus, there’s yet another way to do dependency injection with Spring without XML: annotation-based configuration (available since Spring 2.5, 2007). See http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-annotation-config

    BTW, I find BddMockito makes most of the // GIVEN – WHEN – THEN comments redundant. http://docs.mockito.googlecode.com/hg/org/mockito/BDDMockito.html

    • tamasrev says:

      Thanks, Kofa, for pointing it out! Updated the text around the java-config example.
      If I ever write a book, I’ll ask you to peer-review it 🙂

      Btw, the official BDDMockito examples contains //given – when – then comments too.
      As far as I understood, it replaces the Mockito.when() method with BDDMockito.given() method. So we add given() statements into the // given section. Otherwise they’re the same.

  2. Pingback: Lessons learned from the IoC framework comparison. | tamasrev

  3. Pingback: Testing a subclass-as-wrapper | tamasrev

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