Why Lombok’s @RequiredArgsConstructor Beats Field Injection in Spring
This article explains how Lombok reduces Java boilerplate, compares Spring's field, setter, and constructor injection, and demonstrates using @RequiredArgsConstructor with final fields to create clean, concise constructors without extra annotations, improving code readability and maintainability.
I have seen many developers publicly reject Lombok while secretly adding it to their projects, because it truly simplifies Java code.
Lombok eliminates Java verbosity, shortening code and letting developers focus on important logic. Spring Boot includes Lombok as a dependency, and Java 14 introduced
recordsyntax, similar to:
<code>record Point(int x, int y) {}</code>This article focuses on a less‑known but extremely useful Lombok annotation:
RequiredArgsConstructor.
Explosive Property Injection
Spring offers two basic injection styles, often referred to as three DI approaches: field injection, setter injection, and constructor injection. In practice, developers frequently encounter
byNameand
byTypeinjection, but most modern code uses
@Autowired.
Typical field‑injection code looks like this:
<code>@Service
public class GoodsServiceImpl implements GoodsSrv {
@Autowired
private GoodsRepo goodsRepo;
@Autowired
private TagRepo tagRepo;
@Autowired
private TagRefRepo tagRefRepo;
@Autowired
private BrandRepo brandRepo;
@Autowired
private UnitRepo unitRepo;
}</code>While this works for a few fields, large projects often have dozens of injected beans, leading to massive, unreadable classes where IDEs flag many fields as uninitialized.
Since Spring 4.0, field injection is discouraged because it can hide potential bugs. The recommended approaches are explicit setter injection or constructor injection.
Constructor Injection
Constructor injection becomes the preferred method. A typical implementation:
<code>public class GoodsServiceImpl implements GoodsSrv {
private GoodsRepo goodsRepo;
private TagRepo tagRepo;
private TagRefRepo tagRefRepo;
private BrandRepo brandRepo;
private UnitRepo unitRepo;
public GoodsServiceImpl(
GoodsRepo goodsRepo,
TagRepo tagRepo,
TagRefRepo tagRefRepo,
BrandRepo brandRepo,
UnitRepo unitRepo) {
this.goodsRepo = goodsRepo;
this.tagRefRepo = tagRefRepo;
this.brandRepo = brandRepo;
this.unitRepo = unitRepo;
this.tagRepo = tagRepo;
}
}</code>Although this removes the need for extra annotations, it still requires writing boilerplate constructors.
Lombok’s
AllArgsConstructorgenerates a constructor for all fields, but it may include non‑bean properties, causing Spring to fail.
Using
RequiredArgsConstructorsolves this problem by generating a constructor only for
final(or
@NonNull) fields.
Example:
<code>@Service
@RequiredArgsConstructor
public class GoodsServiceImpl implements GoodsSrv {
private final GoodsRepo goodsRepo;
private final TagRepo tagRepo;
private final TagRefRepo tagRefRepo;
private final BrandRepo brandRepo;
private final UnitRepo unitRepo;
}</code>Marking the fields as
finalforces initialization via the generated constructor, eliminating gray warnings in IDEs.
After applying
@RequiredArgsConstructor, the IDE no longer shows those warnings.
For more advanced usage, you can customize the generated constructor with additional annotations, e.g.:
<code>@RequiredArgsConstructor(onConstructor = @__(@Autowired))</code>This adds
@Autowiredto the Lombok‑generated constructor, though recent Spring versions no longer require it.
By adopting these Lombok techniques, you can dramatically reduce the amount of boilerplate code in your Spring projects.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.