Viktar Patotski Viktar Patotski · · Java & Spring  · 3 min read

Lombok @RequiredArgsConstructor: Spring Boot Constructor Injection

Constructor injection in Spring Boot with Lombok @RequiredArgsConstructor: drop @Autowired, keep fields final, no boilerplate. Setup with Maven and Gradle.

Constructor injection in Spring Boot with Lombok @RequiredArgsConstructor: drop @Autowired, keep fields final, no boilerplate. Setup with Maven and Gradle.

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.

  1. Spring Dependency Injection
  2. Lombok Constructors

Spring Boot service is clean inside but still misses p99? Bottleneck’s usually outside the DI graph - query patterns, GC tuning, autoscaling thresholds. I do performance + scale audits on Spring Boot apps. Free 30-min scan.

Back to Blog

Related Posts

View All Posts »
Performance Viktar Patotski Viktar Patotski · 9 min read

Spring Boot on the JVM vs GraalVM Native: What Actually Wins on AWS

A head-to-head benchmark of the same Spring Boot app built for the JVM and as a GraalVM native binary - on real AWS hardware with a real database, run multiple times. Native wins startup, memory, and predictability; the warm JVM wins the median, peak throughput, and often the tail too - but the JVM swings run-to-run while native stays flat.

Databases Viktar Patotski Viktar Patotski · 13 min read

The Database Scaling Ladder: Every Cheaper Fix Before You Shard

Most teams reach for sharding years before they need it. There is a ladder of cheaper, lower-risk fixes that comes first, and the right rung depends on which bottleneck you actually have: read load, write load, or data size. Here is the ladder, from cheapest to last resort.