« All Software Development Articles

Dependency Injection Overview and Intro to CDI in Java EE 7

12/18/2017

Introduction

This article describes dependency injection (DI) in a Java EE 7 environment. It will provide a brief introduction to DI and cover a few different DI technologies in the Java EE 7 specification. This article will not discuss service location, an alternative to DI also used to apply Inversion of Control. A deeper study on the principle can be found at https://martinfowler.com/articles/injection.html. Not addressed in this article is the more general idea of Dependency Inversion as discussed at https://martinfowler.com/articles/dipInTheWild.html, so abstracting dependencies will not be covered.

Dependencies and Inversion of Control

In an object-oriented application functional responsibilities are broken into classes and these classes by their nature work with each other to produce useful output. The inter-dependencies in between these can become very complex. This is even more so when using a third-party web application container such as Java EE, where we have even less control (or knowledge) of the life-cycle of the application and its components.

Let's start seeing how code forms dependencies and how to invert control. See the bare-bones AlphaRecorder class:


public class AlphaRecorder {
    private Log log;
    
    public AlphaRecorder() {
        log = new Log();
    }
}

The AlphaRecorder is said to have a dependency on the Log class. As the Log class becomes more complex the process to instantiate it does also:


public class AlphaRecorder {
    private Log log;
    
    public AlphaRecorder() {
        LogConfig logConfig = new LogConfig();
        LogFormatter logFormatter = new LogFormatter("X Y Z");
        log = new Log(logConfig, logFormatter);
    }
}

When we need a BetaRecorder and a GammaRecorder we'll be recreating the Log, LogConfig, and LogFormatter every time. An improvement may be to pass the direct dependency into the constructor for these recorders instead of creating them each time:


public class AlphaRecorder {
    private Log log;
    
    public AlphaRecorder(Log aLog) {
        log = aLog;
    }
}


public class Processor {
    public run() {
        LogConfig logConfig = new LogConfig();
        LogFormatter logFormatter = new LogFormatter("X Y Z");
        Log log = new Log(logConfig, logFormatter);
        
        AlphaRecorder a = new AlphaRecorder(log);
        BetaRecorder b = new BetaRecorder(log);
        GammaRecorder c = new GammaRecorder(log);

        a.run();
        b.run();
        c.run();
    }
}

This could be considered a simple form of constructor injection. We've moved the responsibility of creating the recorders' dependencies to the process that creates them. This is the basic idea of Inversion of Control, and constructor injection is a way to accomplish it.

Taking this one level further, could the Processor class declare its dependencies as constructor arguments as well and have even cleaner, more declarative code? Could we get code to look like:


public class Log {
    public (LogConfig aLogConfig, LogFormatter aLogFormatter) {
        // assign to fields
    }
}


public class AlphaRecorder {
    private Log log;
    
    public AlphaRecorder(Log aLog) {
        log = aLog;
    }
}


public class Processor {
    public Processor(AlphaRecorder a, BetaRecorder b, GammaRecorder c) {
        // assign to fields
    }

    public run() {
        a.run();
        b.run();
        c.run();
    }
}

A Deeper Look

"Uncle Bob" said, "Dependency Injection doesn't require a framework; it just requires that you invert your dependencies and then construct and pass your arguments to deeper layers." See https://sites.google.com/site/unclebobconsultingllc/blogs-by-robert-martin/dependency-injection-inversion.

In enterprise development we'll often see containers used such as Java EE or Spring. Dependency injection containers do a lot out of the box. Internally they build a dependency graph. A dependency graph for our example looks like:

As the container runs it creates instances of these classes and provides them to some unit of execution, such as an incoming web request or an event message, as needed. Sometimes new instances are created or old instances are retrieved from memory (singletons, object pools, etc.). By replacing the declaration of injectable classes with interfaces unit testing becomes easier by using mocking, but is outside the scope of this article. Another benefit of injecting by interface is to decrease coupling of code, again not discussed in depth here.

Contexts and Dependency Injection in Java EE

In a Java EE environment an injectable object can be injected into any container managed object. Examples are EJBs, servlets, JSF pages, and even other injectable objects. In the following example we'll create an EJB and a POJO (plain old Java object) and inject them into a servlet.

Let's take a simple declaration of a stateless EJB. It's lifecycle is determined by the normal stateless EJB rules. Without any extra code or annotations EJBs are injectable objects.


import javax.ejb.Stateless;

@Stateless
public class StatelessBean {
    public String getName() {
        return "I'm a stateless EJB";
    }
}

The next object is a POJO class that is annotated with the @RequestScoped annotation. This annotation tells the container to create a new instance on every web request from the client. There are five web application scopes that can be applied in this way, a reference can be found at https://docs.oracle.com/javaee/7/tutorial/cdi-basic008.htm.


import javax.enterprise.context.RequestScoped;

@RequestScoped
public class RequestScopedObject {
    public String getName() {
        return "I'm a request scoped object";
    }
}

The two injectable classes, the EJB and the POJO, can be injected into a servlet using the @Inject annotation on the servlet constructor. The @Inject annotation can be used other places also such as on fields and methods. In the constructor the instances created by the container are assigned to fields and later methods are invoked in the doGet() method.


import java.io.IOException;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/FrontServlet")
public class FrontServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
    private RequestScopedObject requestScopedObject;
    private StatelessBean statelessBean;
       
    @Inject
    public FrontServlet(RequestScopedObject aRequestScopedObject, StatelessBean aStatelessBean) {
        super();
        
        requestScopedObject = aRequestScopedObject;
        statelessBean = aStatelessBean;
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().append("Injected object: " + requestScopedObject.getName());
        response.getWriter().append("\r\nInjected EJB: " + statelessBean.getName());
    }
}

Finally we can verify by deploying the application to our Java EE server and loading the FrontServlet URL. I called my project DependencyInjection and deployed to WildFly 10 from Eclipse (see my WildFly and Eclipse tutorial) so the URL was http://localhost:8080/DependencyInjection/FrontServlet.


Injected object: I'm a request scoped object
Injected EJB: I'm a stateless EJB

Done

You now have a basic understanding of dependency injection and CDI in Java EE. Some topics not covered in this article are Qualifiers, Producer methods, Injecting into JSF, beans.xml, @PostConstruct and @PreDestroy, and more, see https://docs.oracle.com/javaee/7/tutorial/cdi-basic.htm. If you have any feedback or corrections please let me know on my contact page.

Have a nice day.