Introduction
Dependency Injection is a way to create and inject objects by Spring IoC container rather than application objects creating their own dependencies. There are three types of Dependency Injections supported in Spring: Field Injection, Setter Injection, and Constructor Injection. Let's take a simple example of a business class with two dependencies to demonstrate and understand the use of different types of dependency injections.
Example
package com.example.dependencyinjection;
import org.springframework.stereotype.Component;
@Component
public class FirstDependency {
/*
* Member variables and methods
*/
}
package com.example.dependencyinjection;
import org.springframework.stereotype.Component;
@Component
public class SecondDependency {
/*
* Member variables and methods
*/
}
package com.example.dependencyinjection;
import org.springframework.stereotype.Component;
@Component
public class MyBusinessClass {
FirstDependency firstDepenedency;
SecondDependency secondDependency;
public String toString() {
return "My Business Class is using : " + firstDepenedency + " and " + secondDependency;
}
}
package com.example.dependencyinjection;
import java.util.Arrays;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
//If package is not specified @ComponentScan uses current package for scanning Beans
@ComponentScan
public class DependencyInjectionTypes {
public static void main(String[] args) {
try (var context = new AnnotationConfigApplicationContext(DependencyInjectionTypes.class)) {
//List all Beans identified in current package
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println(context.getBean(MyBusinessClass.class));
}
}
}
Output
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
dependencyInjectionTypes
firstDependency
myBusinessClass
secondDependency
My Business Class is using : null and null
Field Injection
Field injection is done by using "@Autowired" annotation on a field. When application context is initialized, Spring automatically injects the appropriate bean into that field. It is the most simple way for injecting dependencies. However, it breaks the immutability of a class as dependencies are not final and can be changed.
Example: Field Injection
package com.example.dependencyinjection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyBusinessClass {
@Autowired
FirstDependency firstDepenedency;
@Autowired
SecondDependency secondDependency;
public String toString() {
return "My Business Class is using : " + firstDepenedency + " and " + secondDependency;
}
}
Output
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
dependencyInjectionTypes
firstDependency
myBusinessClass
secondDependency
My Business Class is using : com.example.dependencyinjection.FirstDependency@6440112d and com.example.dependencyinjection.SecondDependency@31ea9581
Using @Autowired annotation with private final FirstDependency firstDepenedency is not allowed because if a field is marked final it must be initialized at the time of object creation either through constructor or during field declaration.
Setter Injection
This approach uses setter methods to inject dependencies into your class after the object is created. The "@Autowired" annotation is placed on top of setter method to indicate that Spring should inject the dependency.
Example: Setter Injection
package com.example.dependencyinjection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyBusinessClass {
FirstDependency firstDepenedency;
SecondDependency secondDependency;
@Autowired
public void setFirstDepenedency(FirstDependency firstDepenedency) {
this.firstDepenedency = firstDepenedency;
}
@Autowired
public void setSecondDependency(SecondDependency secondDependency) {
this.secondDependency = secondDependency;
}
public String toString() {
return "My Business Class is using : " + firstDepenedency + " and " + secondDependency;
}
}
Output
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
dependencyInjectionTypes
firstDependency
myBusinessClass
secondDependency
My Business Class is using setter injection for : com.example.dependencyinjection.FirstDependency@6c4906d3 and com.example.dependencyinjection.SecondDependency@65987993
Constructor Injection
Constructor injection allows the use of final fields because the dependencies are injected through constructor at the time of object creation. The use of "@Autowired" annotation is not mandatory with constructor injection and the use of final ensures immutability of the injected fields. Hence, this is the most recommended type of dependency injection.
Example: Constructor Injection
package com.example.dependencyinjection;
import org.springframework.stereotype.Component;
@Component
public class MyBusinessClass {
private final FirstDependency firstDepenedency;
private final SecondDependency secondDependency;
public MyBusinessClass(FirstDependency firstDepenedency, SecondDependency secondDependency) {
super();
this.firstDepenedency = firstDepenedency;
this.secondDependency = secondDependency;
}
public String toString() {
return "My Business Class is using constructor injection for : " + firstDepenedency + " and " + secondDependency;
}
}
Output
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
dependencyInjectionTypes
firstDependency
myBusinessClass
secondDependency
My Business Class is using constructor injection for : com.example.dependencyinjection.FirstDependency@6e9a5ed8 and com.example.dependencyinjection.SecondDependency@7e057f43
Summary
This article discusses the three types of Dependency Injections supported in Spring: Field Injection, Setter Injection, and Constructor Injection. It provides a simple example with a business class to demonstrate how dependencies are managed and injected in Spring. The pros and cons of each dependency injection method are briefly touched upon, emphasizing the simplicity of Field Injection, the flexibility of Setter Injection, and the immutability benefits of Constructor Injection.