Introduction to Dependency Injection

Picture of a syringe

Inversion of control (IoC) is a mechanism used to externalise the creation and management of component dependencies.

What a mouthful! What does that mean? Isn’t there a simpler explanation?

The concept is actually very simple: IoC gives a component the objects it needs to do its job.

Any non-trivial application is made up of many classes that work with each other to execute the required business logic. Usually each object is responsible for creating its own references to the objects it works with. This leads to tightly coupled code. Tightly coupled code is difficult to modify, test and reuse. However, without any code coupling, no meaningful work can actually be done. In order to do something useful, classes need to know about each other. A certain amount of coupling is necessary, but this must be carefully controlled.

Who Controls The Dependencies?

Normally we will manually create and control all the connections (dependencies) between various parts of the code. However, when we use an inversion of control container, we ask the container to create and control those interconnections. This architecture inverts control as compared to traditional programming practices.

Using a IoC container, objects are given their dependencies at creation time. The container controls each object in the application. Objects don’t create or obtain their own dependencies. Instead the container creates and controls these dependencies. This is referred to as the container injecting dependencies, hence a dependency injection (DI) container. This is often also called wiring.

One of the benefits of using an IoC container is that we can focus on the business logic. We don’t have to spend a lot of time on the “where and how” of creating and/or referring to a correctly initialised component with all of its dependencies. Dependency injection allows the container to create those references for us, without us having to manually manage all the dependencies.

The terms dependency injection and inversion of control are generally used interchangeably, although dependency injection is just one of a number of ways to implement IoC.

Example

As a concrete example, let’s think about an InvoiceProcessor class that depends on an instance of a DatabaseManager class to load and store the processed invoices.

Using the traditional programming style, the InvoiceProcessor would create an instance of a DatabaseManager either by using the new operator or by getting an instance from a factory class. The InvoiceProcessor would need to know how to create a DatabaseManager instance, what parameters to pass to it, check for nulls, handle exceptions, etc.

public class InvoiceProcessor {    
    // dependency
    private DatabaseManager dbManager; 

    // constructor
    public InvoiceProcessor () {
        // create a DatabaseManager object here
        dbManager = ...;
    }
    // other code ...
}

Using the IoC approach, the DI container creates an instance of DatabaseManager (or one of its subclasses), and then passes this instance to the InvoiceProcessor at runtime. The InvoiceProcessor doesn’t know or care how the DatabaseManager was created; all it knows is that it has a properly initialised DatabaseManager object to work with.

public class InvoiceProcessor {   
    // This is one way to specify dependency injection.
    // Container injects a DatabaseManager instance here.
    @Inject
    private DatabaseManager dbManager;
    // other code using the dbManager
}

Dependency Inversion Principle

The only major restriction we have is to write classes in a way that supports the DI container. We must conform to the Dependency Inversion principle. This is the principle of coding to an interface, not an implementation. An object should only know about its dependencies through their interfaces, and not through their physical implementations. This means that the container can inject any concrete class that implements that specific interface. Different concrete classes can be swapped out without the dependent object knowing the difference.

DI Containers

I’ve been talking about DI containers throughout, but what are they? There are many DI containers for Java. The most popular are the Spring framework and the JEE (Java/Jakarta Enterprise Edition).

JEE was one of the first containers for Java enterprise applications. The first versions of J2EE were heavyweight IoC containers. They only offered dependency lookup, which is harder to use. The later versions of JEE use dependency injection, and have become easier to use.

Spring was one of the first DI frameworks available, and it offered a lighter-weight alternative to J2EE. Spring has grown exponentially. It is now one of the most popular and powerful frameworks. Spring is a more like a framework of frameworks, and provides elegant solutions for just about anything you ever want to do in Java.

Google Guice (pronounced “juice”) is another lightweight DI framework and is largely limited to DI only. Other smaller DI containers include PicoContainer and Dagger (a compile-time DI framework, mainly for Android apps).

Where To Now?

This introduced the concept of dependency injection. Next week I’ll look at DI in more detail.

Tune in for next week’s exciting instalment! Same time, same channel…

Please share your views and comments. And don’t forget to sign up to get our weekly Java tips.

Leave a Comment

Your email address will not be published. Required fields are marked *