Last week I introduced the concept of dependency injection. This week we look at how we implement DI in code.
Implementing Dependency Injection
Dependency injection has three common implementation mechanisms:
Constructor Injection
Constructor Injection provides the class’s dependencies in its constructor(s). We declare a constructor or a set of constructors taking the dependencies as arguments. The DI container passes these dependencies to the class when it instantiates it. Constructor injection is usually used for mandatory (usually immutable) dependencies. Constructor injection is 100% portable between containers.
Setter Injection
Setter Injection provides the class’s dependencies using JavaBean-style setter methods. The DI container uses these setters to inject the dependencies. Setter injection is usually used for optional dependencies. This lets us create reconfigurable objects which can be changed later at runtime by calling the setters. Setter injection is 100% portable between containers.
Field Injection
Field injection is done using annotations on fields. The container uses the Reflection API to access the fields directly. The dependency values are injected directly into the fields of the class when the container instantiates it. Field injection is very easy to use, but is usually container-specific and not portable.
Dependency Injection: Code Examples
In the previous post I used the example of an InvoiceProcessor
that has a dependency on a DatabaseManager
. How does a container wire the dependency? We need to write the class in a way that supports constructor, setter and/or field injection.
Constructor Injection
public class InvoiceProcessor {
// dependency
private DatabaseManager dbManager;
// constructor
public InvoiceProcessor (DatabaseManager dbManager) {
// set the DatabaseManager after data validation code
this.dbManager = dbManager;
}
// other code ...
}
Setter Injection
public class InvoiceProcessor {
// dependency
private DatabaseManager dbManager;
// setter
public setDatabaseManager (DatabaseManager dbManager) {
// set the DatabaseManager after data validation code
this.dbManager = dbManager;
}
// other code ...
}
Field Injection
public class InvoiceProcessor {
// The container injects a DatabaseManager instance
// directly into the field.
@Inject (from JSR 330)
private DatabaseManager dbManager;
// other code ...
}
We could replace the @Inject
annotation with any of the following annotations, depending on the DI container we’re using:
@Resource
(from JSR 250)@Named
(from JSR 299)@Autowired
(Spring specific)
A JEE application server only supports the annotations that are part of the JEE specification (JSRs 250, 299, 330). Spring has its own annotations and supports all the JEE annotations as well.
Wrapping Up
In all three examples above, the DI container creates an instance of DatabaseManager
(or one of its subclasses). It 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 use.
I haven’t shown any container code – that’s too specific for this post. The DI container can create an InvoiceProcessor
instance based on instructions in a deployment descriptor, an XML file, or some other mechanism.
The important point is that we must write our classes in a way that will allow the container to inject the dependency.
Using constructor/setter injection, our classes are always decoupled from the DI container. If we decide to use field injection, we limit ourselves to a specific container.
Join me next week for the last article in this short series on dependency injection.