Introduction
Spring framework is the most popular inversion of control (IoC) implementation concept. In the Java world, we also call this concept - dependency injection (DI). I use the Spring’s DI a lot in my day-to-day work. Therefore I always search for ways to make my code better.
Spring dependency injection variants
Spring offers two major variants of DI:
- Constructor-based
- Setter-based
At first glance, they look similar, besides different syntax. However, there is one crucial difference: constructor-based injection is performed only once, which means it is immutable. But, from my perspective, this limitation is a significant advantage for your software design. It makes sure that you will not be able to create any circular dependencies between spring beans. On the other hand, the setter-based injection can be done an unlimited number of times. As a result, Spring can resolve circular dependencies. Moreover, it can reset already injected beans without you even noticing it. And this can lead to really bad and nasty bugs.
Simple Constructor based DI
So, constructor-based injection is our choice since it is stricter and less error-prone. Let’s take a look at a simple constructor injection example:
package com.patotski.examples.service;
import com.patotski.examples.service.injected.FirstService;
import com.patotski.examples.service.injected.SecondService;
import com.patotski.examples.service.injected.ThirdService;
import org.springframework.stereotype.Service;
@Service
public class ConstructorInjectionService {
private FirstService firstService;
private SecondService secondService;
private ThirdService thirdService;
public ConstructorInjectionService(FirstService firstService,
SecondService secondService,
ThirdService thirdService) {
this.firstService = firstService;
this.secondService = secondService;
this.thirdService = thirdService;
}
}
For my taste, this implementation is a little bit too verbose. Also, every time you change the list of dependencies, you need to update the constructor signature and implementation.
Spring component with Lombok
Let’s enhance this code and simplify our life. To do so, we use code generation provided by project Lombok. Take a look at the example:
package com.patotski.examples.service;
import com.patotski.examples.service.injected.FirstService;
import com.patotski.examples.service.injected.SecondService;
import com.patotski.examples.service.injected.ThirdService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class BestConstructorInjectionService {
private final FirstService firstService;
private final SecondService secondService;
private final ThirdService thirdService;
}
Changes:
- Dependencies became
final
. So, they are required to be initialized no later than in the constructor. - There is no more manually-typed constructor.
- The class received
@RequiredArgsConstructor
annotation.
Changes above do all magic and give us the following features and benefits:
- Lombok’s
@RequiredArgsConstructor
annotation generates a proper constructor every time we update the list of dependencies. This constructor has all required fields as parameters. Therefore you don’t need to worry about the maintenance of this part at all. - Code has fewer lines.
- Such beans are real immutable singletons now because there is no way to reset or replace dependencies. As a result, the code becomes even safer.
That is it. This is how I see the best way of writing spring components with Lombok. All code you can find on my GitHub repo Spring Components With Lombok .
Warning
No one ever creates a constructor with ten arguments in a good software design world, but since the constructor is hidden and generated by Lombok, it may happen unnoticed. There might be the case when you add too many dependency components to your class without noticing it. While this solution is nice and clean, it may theoretically lead to the problem when your spring components violate the single responsibility principle, leading to bad software design. Beware of this and watch out for the amount of private final
fields in the class.