Table of Contents

Design Pattern - Dependency Injection

About

Dependency injection is:

An injection is the passing of a Dependency (ie a service via a variable storing the client's state) to a dependent object (a client).

The control of the flow (dependency construction flow) is reversed because the component does not control the construction of its dependency. This is why dependency injection is also known as Inversion of Control. For purists, dependency injection is only the component assembly part (Not the configuration and lifecycle part) of the Inversion of Control design principle.

Dependency injection is a software design pattern that implements inversion of control and allows a program design to follow the dependency inversion principle and single responsibility principles.

It's a means for obtaining objects (components, bean) where they get their dependencies through:

Before building an object, its dependencies need to be build. And as each dependency is also itself an object, building an object is really building an object graph.

Example

For instance, you can inject a bean into a Servlet by using this annotation.

@EJB myBean ejb;

Purpose

Usage

Inject Service

Dependency injection is often used to inject services, like a logger or a translator.

Test Mock

The object does not look up its dependencies and does not know the location or class of the dependencies. As a result, your classes become easier to test, particularly when the dependencies are on interfaces or abstract base classes, which allow for stub or mock implementations to be used in unit tests.

Example

Manually

The typical pattern is to construct the graph and then call one method on one object to send the flow of control into the object graph.

public static void main(String[] args) throws IOException {

    // Construction code. 
    Greeter greeter = new Greeter(System.out); // This may be many lines that connect many objects
    
    // Behavior code.
    greeter.greet(); // This is one call to one method on one object in the object graph
}

class Greeter {
    public void greet() {
        this.out.println("Hello world!");
    }
    public Greeter(PrintStream out) {
        this.out = out;
    }
    private PrintStream out;
}

Injector

Building object graphs by hand is labour intensive, error prone, and makes testing difficult. Instead, a container or dependency injector can build the object graph for you.

Type

Ways to specify the injection of dependencies into components

How an object can receive a reference to an external module:

The different injector implementations:

Without Injection

public class Client {

    // Internal reference to the service used by this client
    private Service service;

    // Constructor
    Client() {
        // Specify a specific implementation in the constructor instead of using dependency injection
        service = new ServiceExample();
    }

    // Method within this client that uses the services
    public String greet() {
        return "Hello " + service.getName();
    }
}

Constructor Injection

constructor

Constructor Injection is a Dependency Injection variant where a component gets all its dependencies at construction time:

Doc

public class Client {

    // Internal reference to the service used by this client
    private Service service;

    // Constructor
    Client(Service service) {
        // The service is given through a constructor argument
        this.service = service;
    }

    // Method within this client that uses the services
    public String greet() {
        return "Hello " + service.getName();
    }
}

Injection Point:

Setter injection

Setter Injection is a Dependency Injection variant where an object gets all dependencies via setter methods.

the client exposes a setter method that the injector uses to inject the dependency.

Setter Injection is where the container or embedder hands dependencies to a component via setter methods after instantiation.

Setter Injection is idiomatic of the Spring Framework

The disadvantage of Setter Injection there is the possibility to forget to inject some of the dependencies, and the component fail later because of that unset dependency.

This method requires the client to provide a setter method for the dependency.

public class Client {

    // Internal reference to the service used by this client
    private Service service;

    // Constructor
    Client() {
    }

    // Setter method
    public void setService(Service service) {
        // Save the reference to the passed-in service inside this client
        this.service = service;
    }

    // Method within this client that uses the services
    public String greet() {
        return "Hello " + service.getName();
    }
}

Doc: http://picocontainer.com/setter-injection.html

Interface injection

The interface injection: the dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency.

The interface define just how the injector should talk to the client when injecting dependencies.

public interface ServiceSetter {
    public void setService(Service service);
}
public class Client implements ServiceSetter {
    // Internal reference to the service used by this client.
    private Service service;

    // Set the service that this client is to use.
    @Override
    public void setService(Service service) {
        this.service = service;
    }
}

Framework / Library

See Dependency - Injector

Documentation / Reference