don't worry, it's probably fine

Build Your Own Dependency Injection Library - Part 1

02 Nov 2020

nablopomo java

Dependency injection is common in modern software projects. It allows the programmer to separate classes from the objects that they depend on - not needing to know about their implementations, only their interfaces. This can lead us to more loosely coupled, easily maintainable code.

A lot of projects in the Java ecosystem use a dependency injection container, such as Guice, Dagger, or Picocontainer. These are libraries or frameworks that provide the ability to register components and instantiate them on demand.

This is a series of blogposts where we will step through building our own dependency injection library, using Test-Driven Development (with whatever testing framework you like!) and implementing the library itself using only the Java standard library.

The content of these posts come from the learnings of building femto, my own teeny-tiny dependency injection library.

Let’s get started!

Zero, One, Many

We will approach this project using the technique of “zero, one, many” - start with the smallest example cases, and slowly add complexity by driving the implementation with small tests.

Let’s first consider, then, how to instantiate a Class<T> that has no dependents.

class Zero {
    public String say() {
        return "Hello, world!";
    }
}

The main tool available to us at this stage is that java.lang.Class has a newInstance() method that takes zero arguments. That is exactly what we want for this case.

Our first test, then, will be use our injector to create a new instance of Zero.class.

class ZeroTest {
    @Test
    void canInstantiateAZeroArgumentClass() {
        var injector = new TinyInjector();

        var instance = injector.get(Zero.class);

        assertThat(instance.say(), is("Hello, world!"));
    }   
}

The injector will use .newInstance() to create a version of this class.

This method is deprecated as of Java 9, but it serves our purposes for now (and it will get refactored away in future changes - spoilers!). It throws checked exceptions - for the time being we’ll rethrow these as unchecked exceptions.

class TinyInjector {
    public <T> T get(Class<T> klass) {
        try {
            return klass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

You can see the worked through code for this post in a GitHub Gist, pop it in your own project and observe the tests passing.

We’ll leave it there for now, and in the next post we’ll pick up registering multiple classes for objects that require dependencies.


November is National Blog Posting Month, or NaBloPoMo. I’ll be endeavouring to write one blog post per day in the month of November 2020 - some short and sweet, others long and boring.