Dependency injection (DI). This article describes the concept of dependency injection from a Java perspective.
1. Dependency injection in Java
1.1. What is dependency injection?
Dependency injection (DI) is the concept in which objects get other required objects from outside.
DI can be implemented in any programming language. The general concept behind dependency injection is called Inversion of Control.
A Java class has a dependency on another class, if it uses an instance of this class. We call this a class dependency. For example, a class which accesses a logger service has a dependency on this service class.
Ideally Java classes should be as independent as possible from other Java classes. This increases the possibility of reusing these classes and to be able to test them independently from other classes.
If the Java class creates an instance of another class via the new
operator, it cannot be used (and tested) independently from this class and this is called a hard dependency.
The following example shows a class which has no hard dependencies.
package com.vogella.tasks.ui.parts;
import java.util.logging.Logger;
public class MyClass {
private Logger logger;
public MyClass(Logger logger) {
this.logger = logger;
// write an info log message
logger.info("This is a log message.");
}
}
Please note that this class is just a normal Java class, there is nothing special about it, except that it avoids direct object creation. |
A framework class, usually called the dependency container, could analyze the dependencies of this class. With this analysis it is able to create an instance of the class and inject the objects into the defined dependencies, via Java reflection.
This way the Java class has no hard dependencies, which means it does not rely on an instance of a certain class. This allows you to test your class in isolation, for example by using mock objects.
Mock objects (mocks) are objects which behave similar as the real object. But these mocks are not programmed; they are configured to behave in a certain predefined way. Mock is an English word which means to mimic or to imitate.
If dependency injection is used, a Java class can be tested in isolation.
1.2. Using annotations to describe class dependencies
Different approaches exist to describe the dependencies of a class. The most common approach is to use Java annotations to describe the dependencies directly in the class.
The standard Java annotations for describing the dependencies of a class were defined in the Java Specification Request 330 (JSR330).
This specification describes the @Inject
and @Named
annotations.
The latest version of these annotations are provided by the jakarta project.
The following listing shows a class which uses annotations to describe its dependencies.
import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
public class MyPart {
@Inject private Logger logger;
// inject class for database access
@Inject private DatabaseAccessClass dao;
@PostConstruct
public void createControls(Composite parent) {
logger.info("UI will start to build");
Label label = new Label(parent, SWT.NONE);
label.setText("Eclipse 4");
Text text = new Text(parent, SWT.NONE);
text.setText(dao.getNumber());
}
}
Please note that this class uses the new operator for the user interface components.
This implies that this part of the code is nothing you plan to replace via your tests.
In this case you made the decision to have a hard coupling to the corresponding user interface toolkit.
|
1.3. Where can objects be injected into a class according to JSR330?
Dependency injection can be performed on:
-
the constructor of the class (construction injection)
-
a field (field injection)
-
the parameters of a method (method injection)
It is possible to use dependency injection on static and on non-static fields and methods. Avoiding dependency injection on static fields and methods is a good practice, as it has the following restrictions and can be hard to debug.
-
Static fields will be injected after the first object of the class was created via DI, which means no access to the static field in the constructor
-
Static fields can not be marked as final, otherwise the compiler or the application complains at runtime about them
-
Static methods are called only once after the first instance of the class was created
1.4. Order in which dependency injection is performed on a class
According to the specification the injection is done in the following order:
-
constructor injection
-
field injection
-
method injection
The order in which the methods or fields annotated with @Inject
are called is not defined.
You cannot assume that the methods or fields are called in the order of their declaration in the class.
As fields and method parameters are injected after the constructor is called, you cannot use injected member variables in the constructor. |
2. Java and dependency injection frameworks
You can use dependency injection without any additional framework by providing classes with sufficient constructors or getter and setter methods.
A dependency injection framework simplifies the initialization of the classes with the correct objects.
Two popular dependency injection frameworks are Spring and Google Guice.
The usage of the Spring framework for dependency injection is described in Dependency Injection with the Spring Framework - Tutorial.
Also Eclipse RCP is using dependency injection.
3. Links and Literature
3.2. vogella Java example code
If you need more assistance we offer Online Training and Onsite training as well as consulting