Best Lombok Annotations for Spring Boot Projects
Problem
When I write Spring Boot services, I face a lot of boilerplate code:
@Servicepublic class UserService { private static final Logger log = LoggerFactory.getLogger(UserService.class);
private final UserRepository userRepository; private final EmailService emailService; private final AuditService auditService;
// Boilerplate constructor - 8 lines for 3 dependencies public UserService(UserRepository userRepository, EmailService emailService, AuditService auditService) { this.userRepository = userRepository; this.emailService = emailService; this.auditService = auditService; }
public User createUser(String email, String name) { log.info("Creating user with email: {}", email); // ... }}Every service has:
- Manual logger initialization
- Repetitive constructor for dependency injection
- Getter/setter boilerplate for entities
I wanted to reduce this clutter.
Environment
- Spring Boot 3.x
- Lombok 1.18.x
- IntelliJ IDEA with Lombok plugin
What happened?
I tried using @Data everywhere to eliminate boilerplate:
@Entity@Data // Includes @EqualsAndHashCode!public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
private String name; private BigDecimal price;
@OneToMany(mappedBy = "product", fetch = FetchType.LAZY) private Set<Order> orders;}But this caused issues:
equals()triggered lazy loading ofordershashCode()changed when ID was assigned- Duplicate entities appeared in Sets
I needed to understand which annotations to use and when.
How to solve it?
I found four key annotations that work best in Spring Boot:
1. @RequiredArgsConstructor for Dependency Injection
@Service@Slf4j@RequiredArgsConstructorpublic class UserService { private final UserRepository userRepository; private final EmailService emailService; private final AuditService auditService;
// No manual constructor needed! // Lombok generates it for all final fields
public User createUser(String email, String name) { log.info("Creating user with email: {}", email);
return User.builder() .email(email) .name(name) .createdAt(LocalDateTime.now()) .active(true) .build(); }}Spring automatically uses Lombok’s generated constructor. No @Autowired needed when there’s only one constructor.
2. @Slf4j for Logging
One annotation replaces this:
private static final Logger log = LoggerFactory.getLogger(UserService.class);With this:
@Slf4jpublic class UserService { // 'log' field is auto-generated}3. @Builder for Complex Objects
Instead of 50+ lines of manual builder code:
@Getter@Setter@Builderpublic class User { private String email; private String name;
@Builder.Default private LocalDateTime createdAt = LocalDateTime.now();
@Builder.Default private boolean active = true;}Usage:
User user = User.builder() .name("Test User") .build();4. Selective @Getter/@Setter for JPA Entities
Avoid @Data on JPA entities:
@Entity@Getter@Setter@NoArgsConstructorpublic class Customer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
private String email; private String name;
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL) private List<Order> orders = new ArrayList<>();
// Manual equals/hashCode for JPA best practices @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Customer)) return false; Customer customer = (Customer) o; return id != null && id.equals(customer.id); }
@Override public int hashCode() { return getClass().hashCode(); }}The reason
I think the key insights are:
-
@Data includes too much: It adds
equals(),hashCode(),toString()which cause JPA issues. -
Constructor injection is Spring’s recommended approach:
@RequiredArgsConstructormakes it effortless. -
Final fields enforce immutability: Dependencies cannot be changed after initialization, improving thread safety.
-
Selective annotations give control: Use exactly what you need, not everything.
Summary
In this post, I showed the best Lombok annotations for Spring Boot. The key point is: use @RequiredArgsConstructor for dependency injection, @Slf4j for logging, @Builder for complex objects, and selective @Getter/@Setter instead of broad @Data. This reduces boilerplate while maintaining clean, testable code.
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 Documentation
- 👨💻 Spring Framework Documentation
- 👨💻 Reddit Discussion: Lombok in Spring Boot Projects
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments