About
Dependency injection is:
- a dependency resolution mechanism
- where components (object, bean, client) are given their (type) dependencies (service, ..) (Dependencies are injected)
- and therefore are not creating (finding, instantiating) them directly.
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:
- their constructors arguments,
- methods,
- arguments to a factory method,
- properties that are set on the object instance after it is constructed
- or directly into fields.
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.
Articles Related
Example
For instance, you can inject a bean into a Servlet by using this annotation.
@EJB myBean ejb;
Purpose
- The purpose of Dependency Injection is to reduce coupling in your application to make it more flexible and easier to test, ensure that components are loosely coupled.
- The core principle is to separate behavior from dependency resolution (the same than composition)
- Objects don't have hard coded dependencies. If you need to change the implementation of a dependency, all you have to do is Inject a different type of Object.
- Dependency injection is beneficial to most nontrivial applications.
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:
- constructor_injection: the dependencies are provided through a class constructor.
- setter_injection: the client exposes a setter method that the injector uses to inject the dependency.
- 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.
- annotated_injection Methods are marked with an annotation to indicate that they should be used for injecting dependencies
The different injector implementations:
- factories,
- service locators,
- dependency injection containers - It's a library that takes care of keeping dependencies in a registry, instantiating them on demand, and making them configurable
Without Injection
- An example without dependency 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 Injection is a Dependency Injection variant where a component gets all its dependencies at construction time:
- via its signature
- or field/method injection
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:
- Annotation:
- Method: Methods are marked with an annotation to indicate that they should be used for injecting dependencies
- Field: Fields are marked with an annotation to indicate that they should be injected into
- Named:
- Named Field - Fields of a certain name should be injected into
- Named Method - If method names match other component names, injection happens
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();
}
}
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.
- Service setter interface.
public interface ServiceSetter {
public void setService(Service service);
}
- Client class
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;
}
}