Constructor dependency injection in Spring Framework
Spring framework provides inversion of control through Constructor based and Setter based Dependency Injection(DI). In this article, we will explore more about the first kind.
What is Constructor based DI in Spring
To simply put, When we pass the required objects as constructor arguments at the time of bean creation, then we call it the constructor based dependency injection. But let’s explain this with an example.
Let’s say you are developing an OrderService that requires PaymentService to process payments. In this case object of paymentService is a dependency for OrderService. With that in mind, You can create an OrderService as shown below.
PaymentService paymentService = new PaymentService();
OrderService orderService = new OrderService(paymentService);
orderService.performOrder();
Code language: Java (java)
But the problem here is that whenever you create a payment service, You as a developer need to create the objects and pass them as parameters. But with constructor DI, You can define the beans and spring will take care of the bean creation. Let’s understand how to do this in different ways.
For this first you need to include Spring context to your project as a maven library.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.5</version>
</dependency>
Code language: HTML, XML (xml)
XML configuration for Constructor Dependency Injection
The XML configurations are the traditional way to define beans in a Spring application. But, it gives a better idea of how a context work So let’s understand this with an example.
For a constructor DI, We need to use the constructor-arg element in your XML. This element takes a primitive value, reference to another bean or a bean as a child element. Let’s see an example of each.
First let’s take a look at creating beans with primitive types as arguments for the constructor. for example, The following creates a Cow that goes Moo, Moo!
<bean class="com.springhow.beans.Animal">
<constructor-arg index="0" value="Cow"/>
<constructor-arg index="1" value="Moo, Moo...!"/>
</bean>
Code language: HTML, XML (xml)
Here, the index attribute makes sure that the spring container injects constructor arguments in the correct order.
Next, you can also pass other beans as constructor parameters.
<!-- define paymentService bean beforehand -->
<bean id="paymentService" class="com.springhow.beans.PaymentService"/>
<bean id="orderService" class="com.springhow.beans.OrderService">
<!-- Use the paymentService as a reference -->
<constructor-arg ref="paymentService"/>
<constructor-arg>
<!-- You can create define the bean as and when -->
<bean class="com.springhow.beans.UserService"/>
</constructor-arg>
</bean>
Code language: HTML, XML (xml)
Notice how you can pass another bean as a constructor argument. Also, if you need to you can create a bean local to that bean definition. In this case, We created an UserService bean and used it within the constructor definition.
Spring Annotation config for Constructor Dependency Injection
The annotation-based configuration is the new and probably best way to define spring beans at the moment. With the improvements to Bean autowiring since Spring Framework 5, annotations are the most preferred way of defining Spring beans. If you are using Spring Boot, This is the way to go.
With annotations, You can define simple Spring Beans using constructor arguments as shown below.
@Component
public class OrderService {
private final PaymentService paymentService;
private final UserService userService;
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
this.userService = new UserService();
}
@Autowired
public OrderService(PaymentService paymentService, UserService userService) {
this.paymentService = paymentService;
this.userService = userService;
}
void performOrder() {
userService.setupUser();
paymentService.performPayment();
}
}
Code language: Java (java)
Here @Component lets the spring annotation process know that OrderService is a bean. And it requires two dependencies of type PaymentService and UserService for its constructor. If the required beans are available in the container, then the Spring container will create OrderService accordingly.
Also, Spring Framework is lenient on constructor arguments and thus you can define multiple constructors for the same bean. When multiple constructors are available, Spring Container will use the most satisfied constructor. For example, If you haven’t defined any userService, Spring will use the first constructor to create the OrderService bean.
We can test out setup using the following @Configuration class and a simple AnnotationConfigApplicationContext.
@Configuration
public class BeanConfig {
@Bean
PaymentService getPaymentService() {
return new PaymentService();
}
@Bean
UserService getUserService() {
return new UserService();
}
}
Code language: Java (java)
public class DependencyAnnotationExample {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
OrderService bean = context.getBean(OrderService.class);
bean.performOrder();
}
}
Code language: Java (java)
And as of version 5, Spring Framework can infer constructor arguments. Due to this, you can drop the @Autowired completely for all constructor based injection.
Why Constructor Injection is important?
Constructor Injection is best suitable when you need to specify mandatory dependencies. Also, constructors let you create immutable components as the dependencies are usually unchanged after constructor initialization.
As developers tend to keep fewer constructor arguments, the components are cleaner and easier to maintain. This way, You are more likely to create components of single responsibility. This means we achieve the separation of concerns.
Furthermore, constructor injected beans are usually fully initialized for use. So, you are less likely to have side effects thus less bugs.
If you like this article, you may also like to our other reads below.