Lombok Annotations in Java: The Complete Guide for Spring Boot Developers
Lombok removes the boilerplate that makes Java classes twice as long as they need to be. If you work in Spring Boot, you’ve seen it everywhere. But most developers use the same three annotations and never look at the rest.
Here’s every Lombok annotation that matters, when to use each one, and when Java Records make them unnecessary.
The Core Annotations
@Getter and @Setter
Generate getter and setter methods for your fields. Put them on the class to cover all fields, or on individual fields for fine-grained control.
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Customer {
private String name;
private String email;
private int accountAge;
}
This generates getName(), getEmail(), getAccountAge(), setName(), setEmail(), and setAccountAge(). Six methods you didn’t write.
You can also control access levels:
@Getter
@Setter(AccessLevel.PROTECTED)
private String internalId;
@NoArgsConstructor and @AllArgsConstructor
@NoArgsConstructor generates an empty constructor. @AllArgsConstructor generates one that takes every field as a parameter.
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
public class Order {
private String orderId;
private String customerName;
private double total;
private String status;
}
Spring and JPA need no-arg constructors for proxying and deserialization. Instead of writing an empty constructor that does nothing, this annotation handles it.
@AllArgsConstructor is useful for test factories and builder patterns. But be careful: if you add a field, the constructor signature changes and every call site breaks. For classes that change often, @Builder is safer.
@RequiredArgsConstructor
Generates a constructor for final fields and fields annotated with @NonNull only. This is the one you want for Spring dependency injection:
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentGateway paymentGateway;
private final NotificationService notificationService;
}
Spring injects all three dependencies through the generated constructor. No @Autowired needed. This is cleaner than field injection and makes the class easy to test — just pass mocks to the constructor.
@ToString
Generates a toString() method that includes all fields. Useful for logging and debugging.
import lombok.ToString;
@ToString
public class Customer {
private String name;
private String email;
@ToString.Exclude
private String ssn;
}
@ToString.Exclude keeps sensitive fields out of logs. You can also use @ToString(onlyExplicitlyIncluded = true) and mark fields with @ToString.Include for a whitelist approach.
Watch out for circular references in entities with bidirectional relationships. Two entities that include each other in toString() will cause a StackOverflowError. Always exclude the “many” side of a relationship.
@EqualsAndHashCode
Generates equals() and hashCode() based on all fields. Use it carefully with JPA entities:
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Entity
public class Product {
@EqualsAndHashCode.Include
@Id
private Long id;
private String name;
private double price;
}
For JPA entities, only include the ID field or a natural business key. Including all fields causes problems with lazy-loaded proxies and detached entities. This is a common source of bugs when using @Data on entities.
The Power Annotations
@Data
A shortcut that combines @Getter, @Setter, @ToString, @EqualsAndHashCode, and @RequiredArgsConstructor. One annotation instead of five.
import lombok.Data;
@Data
public class CustomerDTO {
private final String id;
private String name;
private String email;
private String phone;
}
Use @Data on DTOs and value objects. Do NOT use it on JPA entities — the generated equals/hashCode will cause issues with lazy loading and proxy objects. Use explicit annotations on entities instead.
@Value
The immutable version of @Data. Makes all fields private final, generates getters (no setters), and includes toString, equals, hashCode, and an all-args constructor.
import lombok.Value;
@Value
public class Money {
String currency;
long amountInCents;
}
This is Lombok’s answer to immutable value objects. In Java 17+, consider using Records instead — they do the same thing with language-level support.
@Builder
Generates a builder pattern for your class. This is the annotation that saves the most time on complex objects.
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class SearchCriteria {
private String query;
@Builder.Default
private int page = 1;
@Builder.Default
private int size = 20;
private String sortBy;
private String sortDirection;
private List<String> filters;
}
// Usage
SearchCriteria criteria = SearchCriteria.builder()
.query("spring boot")
.size(50)
.sortBy("relevance")
.build();
@Builder.Default sets default values when the builder doesn’t specify them. Without it, all fields default to null/0/false.
@Builder is particularly useful for test data:
Order testOrder = Order.builder()
.orderId("test-123")
.customerName("Test Customer")
.total(99.99)
.status("PENDING")
.build();
@Slf4j
Adds a log field to your class. No more private static final Logger log = LoggerFactory.getLogger(MyClass.class).
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class PaymentService {
public void processPayment(Payment payment) {
log.info("Processing payment: {}", payment.getId());
// business logic
log.debug("Payment processed successfully");
}
}
There are variants for other logging frameworks: @Log4j2, @CommonsLog, @JBossLog. Use whichever matches your project.
@SneakyThrows
Throws checked exceptions without declaring them. Use sparingly.
import lombok.SneakyThrows;
@SneakyThrows
public String readFile(String path) {
return Files.readString(Path.of(path));
}
This compiles without a throws IOException declaration. The exception still gets thrown at runtime — Lombok just tricks the compiler. Some teams ban this annotation because it hides exception contracts. Use your judgment.
Lombok vs Java Records
Java 16 introduced Records, which overlap with Lombok’s @Value and @Data for simple data carriers.
// Lombok @Value
@Value
public class Point {
int x;
int y;
}
// Java Record (no Lombok needed)
public record Point(int x, int y) {}
Both generate constructors, getters, toString, equals, and hashCode. Records are built into the language. No annotation processor. No IDE plugin.
Use Records when:
- The class is a simple data carrier
- You don’t need inheritance
- You don’t need a builder
- All fields are final
Keep Lombok when:
- You need
@Builder - You need mutable objects (
@Data) - You need
@Slf4jon service classes - You need
@RequiredArgsConstructorfor Spring DI - You need custom
equals/hashCodeon JPA entities
In practice, most Spring Boot projects use both. Records for DTOs and value objects, Lombok for everything else.
Setup
Add Lombok to your Spring Boot project:
<!-- Maven -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
// Gradle
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
Spring Boot manages the Lombok version through its dependency management, so you don’t need to specify a version.
Make sure your IDE has the Lombok plugin installed. IntelliJ bundles it now. For Eclipse, run the Lombok installer jar.
Common Mistakes
Using @Data on JPA entities. The generated equals and hashCode methods cause problems with lazy-loaded proxies. Use @Getter, @Setter, and @EqualsAndHashCode(onlyExplicitlyIncluded = true) separately on entities.
Forgetting @Builder.Default. Without it, builder-created objects have null/0/false for all unset fields, even if your field declaration has a default value.
Circular @ToString on bidirectional relationships. Two entities that include each other in toString will recurse infinitely. Use @ToString.Exclude on one side.
Not updating the annotation processor. If your build fails after a Java upgrade, update Lombok first. Version 1.18.30+ supports Java 21 and 22. Version 1.18.34+ supports Java 25.
If your team is working through a Java 8 to 17 migration, now is a good time to decide which Lombok annotations to keep and which to replace with Records. The OpenRewrite migration tool can help automate some of these conversions.
For more on reducing boilerplate in your Java projects, see our guides on Spring Data JPA best practices and Spring Boot REST API patterns.
Java Modernization Readiness Assessment
15 questions your team should answer before starting a migration. Takes 10 minutes. Could save you months.