Skip to content

How to Use @RequiredArgsConstructor for Spring Boot DI

Problem

When I write Spring Boot services with multiple dependencies, the constructor boilerplate grows:

OrderController.java (manual constructor)
@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:

OrderController.java (field injection)
@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:

OrderController.java (clean approach)
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/orders")
@RequiredArgsConstructor
public 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:

Generated constructor (what Lombok produces)
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:

Adding new dependency
@RequiredArgsConstructor
public class OrderController {
// ... existing dependencies ...
private final NewService newService; // Just add this!
// No constructor update needed
}

Using @NonNull Fields

You can also include @NonNull fields:

AdvancedService.java
import lombok.RequiredArgsConstructor;
import lombok.NonNull;
@Service
@RequiredArgsConstructor
public 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:

Generated null check
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:

ComplexService.java (multiple constructors)
@Service
@RequiredArgsConstructor
public 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:

  1. Best Practice Compliance: Constructor injection is Spring’s recommended approach.

  2. Immutability: Final fields cannot be changed after initialization.

  3. Testability: Easy to instantiate with mock dependencies:

Test example
@Test
void testOrderController() {
OrderController controller = new OrderController(
mockOrderService,
mockPaymentService,
mockNotificationService,
mockInventoryService,
mockShippingService
);
// No Spring context needed!
}
  1. Compile-Time Safety: Missing dependencies are caught immediately.

  2. 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:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments