How to Use @RequiredArgsConstructor for Spring Boot DI
Problem
When I write Spring Boot services with multiple dependencies, the constructor boilerplate grows:
@RestController@RequestMapping("/api/orders")public class OrderController { private final OrderService orderService; private final PaymentService paymentService; private final NotificationService notificationService; private final InventoryService inventoryService; private final ShippingService shippingService;
// 10+ lines of constructor boilerplate! public OrderController(OrderService orderService, PaymentService paymentService, NotificationService notificationService, InventoryService inventoryService, ShippingService shippingService) { this.orderService = orderService; this.paymentService = paymentService; this.notificationService = notificationService; this.inventoryService = inventoryService; this.shippingService = shippingService; }}Every new dependency means updating the constructor. It’s repetitive and error-prone.
Environment
- Spring Boot 3.x
- Lombok 1.18.x
- Maven/Gradle with Lombok dependency
What happened?
I tried using @Autowired field injection to avoid constructors:
@RestController@RequestMapping("/api/orders")public class OrderController { @Autowired private OrderService orderService;
@Autowired private PaymentService paymentService;
@Autowired private NotificationService notificationService;}But this has problems:
- Spring team recommends constructor injection over field injection
- Harder to test (requires reflection or Spring context)
- Dependencies not visible in constructor
- Cannot use final fields
I needed a better approach.
How to solve it?
I found @RequiredArgsConstructor - Lombok generates constructors for all final fields:
import lombok.RequiredArgsConstructor;import org.springframework.web.bind.annotation.*;
@RestController@RequestMapping("/api/orders")@RequiredArgsConstructorpublic class OrderController { private final OrderService orderService; private final PaymentService paymentService; private final NotificationService notificationService; private final InventoryService inventoryService; private final ShippingService shippingService;
// Adding a new dependency? Just add a final field! private final AnalyticsService analyticsService;
// No constructor needed! // Lombok generates it automatically}How It Works
Lombok generates this at compile-time:
public OrderController(OrderService orderService, PaymentService paymentService, NotificationService notificationService, InventoryService inventoryService, ShippingService shippingService, AnalyticsService analyticsService) { this.orderService = orderService; this.paymentService = paymentService; this.notificationService = notificationService; this.inventoryService = inventoryService; this.shippingService = shippingService; this.analyticsService = analyticsService;}Spring sees this constructor and auto-wires it. No @Autowired needed when there’s only one constructor.
Adding a Dependency
Just add a new final field:
@RequiredArgsConstructorpublic class OrderController { // ... existing dependencies ...
private final NewService newService; // Just add this! // No constructor update needed}Using @NonNull Fields
You can also include @NonNull fields:
import lombok.RequiredArgsConstructor;import lombok.NonNull;
@Service@RequiredArgsConstructorpublic class AdvancedService { private final UserRepository userRepository;
@NonNull private String configValue; // Also included, with null check!
private String optionalField; // NOT included - not final or @NonNull}Lombok adds null validation:
public AdvancedService(UserRepository userRepository, @NonNull String configValue) { if (configValue == null) { throw new NullPointerException("configValue is marked @NonNull but is null"); } this.userRepository = userRepository; this.configValue = configValue;}Multiple Constructors
If you have multiple constructors, use @Autowired to specify which one Spring should use:
@Service@RequiredArgsConstructorpublic class ComplexService { private final MainRepository repository;
// Lombok generates this - tell Spring to use it @Autowired public ComplexService(MainRepository repository) { this.repository = repository; }
// Alternative constructor for testing public ComplexService() { this.repository = new InMemoryRepository(); }}The reason
I think the key benefits are:
-
Best Practice Compliance: Constructor injection is Spring’s recommended approach.
-
Immutability: Final fields cannot be changed after initialization.
-
Testability: Easy to instantiate with mock dependencies:
@Testvoid testOrderController() { OrderController controller = new OrderController( mockOrderService, mockPaymentService, mockNotificationService, mockInventoryService, mockShippingService ); // No Spring context needed!}-
Compile-Time Safety: Missing dependencies are caught immediately.
-
Reduced Boilerplate: No manual constructor code to maintain.
Summary
In this post, I showed how to use @RequiredArgsConstructor for Spring Boot dependency injection. The key point is: declare dependencies as final fields, add @RequiredArgsConstructor, and Spring auto-wires without @Autowired. This eliminates constructor boilerplate while following Spring’s recommended injection pattern.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Project Lombok: @RequiredArgsConstructor
- 👨💻 Spring Framework Constructor Injection
- 👨💻 Reddit Discussion: Lombok in Spring Boot
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments