Lombok is a Java library that helps reduce boilerplate code by automatically generating common constructs like getters, setters, constructors, and more through annotations. When combined with Spring Boot, it can significantly simplify your codebase.
Table of Contents
- Setup Lombok in Spring Boot
- Core Lombok Annotations
- Spring-Specific Lombok Annotations
- Logging Annotations
- Best Practices
- Complete Example
1. Setup Lombok in Spring Boot
Maven Dependency
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version> <!-- Use latest version -->
<scope>provided</scope>
</dependency>
IDE Setup
- Install Lombok plugin for your IDE (IntelliJ, Eclipse, etc.)
- Enable annotation processing in your IDE settings
2. Core Lombok Annotations
@Getter and @Setter
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class User {
private Long id;
private String username;
private String email;
private boolean active;
}
// Equivalent to writing all getters and setters
@ToString
import lombok.ToString;
@ToString
public class User {
private Long id;
private String username;
// ...
}
// Generates: public String toString() { return "User(id=" + this.id + ", username=" + this.username + ")"; }
@EqualsAndHashCode
import lombok.EqualsAndHashCode;
@EqualsAndHashCode
public class User {
private Long id;
private String username;
// ...
}
// Generates equals() and hashCode() methods
@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor
import lombok.*;
@NoArgsConstructor
@RequiredArgsConstructor
@AllArgsConstructor
public class User {
@NonNull private Long id;
private String username;
private String email;
}
// Usage:
// User u1 = new User(); // NoArgsConstructor
// User u2 = new User(1L); // RequiredArgsConstructor (for @NonNull fields)
// User u3 = new User(1L, "john", "john@example.com"); // AllArgsConstructor
@Data
Combines @Getter
, @Setter
, @ToString
, @EqualsAndHashCode
, and @RequiredArgsConstructor
import lombok.Data;
@Data
public class Product {
private Long id;
private String name;
private double price;
}
@Builder
import lombok.Builder;
@Builder
public class Order {
private Long id;
private String productName;
private int quantity;
private double price;
}
// Usage:
// Order order = Order.builder()
// .id(1L)
// .productName("Laptop")
// .quantity(1)
// .price(999.99)
// .build();
@Value
Immutable version of @Data
(all fields are made private and final)
import lombok.Value;
@Value
public class ImmutableUser {
Long id;
String username;
String email;
}
@Slf4j
(See logging section below)
3. Spring-Specific Lombok Annotations
@RequiredArgsConstructor + @Autowired
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
// Constructor with all final fields is automatically generated
// and @Autowired is automatically applied in Spring
}
@With
Creates a clone of the object with one field changed (useful for immutable objects)
import lombok.With;
@With
public class ImmutableConfig {
private final String host;
private final int port;
}
// Usage:
// ImmutableConfig config = new ImmutableConfig("localhost", 8080);
// ImmutableConfig newConfig = config.withPort(9090);
4. Logging Annotations
@Slf4j
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class OrderService {
public void processOrder(Order order) {
log.info("Processing order: {}", order);
try {
// business logic
} catch (Exception e) {
log.error("Failed to process order {}", order.getId(), e);
}
}
}
Other logging variants:
@CommonsLog
(Apache Commons Logging)@Log
(java.util.logging)@Log4j
(Log4j)@Log4j2
(Log4j2)@XSlf4j
(Extended SLF4J Logger)
5. Best Practices
- Use
@Data
carefully - It generates all setters which might not be desired for immutable objects - Prefer
@RequiredArgsConstructor
for Spring beans - Makes dependency injection clear - Combine
@Value
with@Builder
for immutable DTOs - Provides both immutability and easy construction - Avoid Lombok for complex methods - If method logic isn't straightforward, implement it manually
- Be explicit with
@ToString
exclusions - Use@ToString.Exclude
for sensitive data - Document Lombok usage - Not all developers may be familiar with Lombok
6. Complete Example: Spring Boot Application with Lombok
Entity Class
import lombok.*;
import javax.persistence.*;
import java.time.LocalDateTime;
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BlogPost {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false, length = 5000)
private String content;
private String author;
@Column(updatable = false)
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
}
Repository Interface
import org.springframework.data.jpa.repository.JpaRepository;
public interface BlogPostRepository extends JpaRepository<BlogPost, Long> {
}
Service Layer
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class BlogPostService {
private final BlogPostRepository blogPostRepository;
public BlogPost createPost(BlogPost post) {
log.info("Creating new blog post: {}", post.getTitle());
return blogPostRepository.save(post);
}
public List<BlogPost> getAllPosts() {
return blogPostRepository.findAll();
}
public BlogPost getPostById(Long id) {
return blogPostRepository.findById(id)
.orElseThrow(() -> new PostNotFoundException("Post not found with id: " + id));
}
public BlogPost updatePost(Long id, BlogPost postDetails) {
BlogPost post = getPostById(id);
post.setTitle(postDetails.getTitle());
post.setContent(postDetails.getContent());
post.setAuthor(postDetails.getAuthor());
return blogPostRepository.save(post);
}
public void deletePost(Long id) {
blogPostRepository.deleteById(id);
}
}
@RequiredArgsConstructor
class PostNotFoundException extends RuntimeException {
private final String message;
}
Controller
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.net.URI;
import java.util.List;
@RestController
@RequestMapping("/api/posts")
@RequiredArgsConstructor
public class BlogPostController {
private final BlogPostService blogPostService;
@GetMapping
public ResponseEntity<List<BlogPost>> getAllPosts() {
return ResponseEntity.ok(blogPostService.getAllPosts());
}
@GetMapping("/{id}")
public ResponseEntity<BlogPost> getPostById(@PathVariable Long id) {
return ResponseEntity.ok(blogPostService.getPostById(id));
}
@PostMapping
public ResponseEntity<BlogPost> createPost(@RequestBody BlogPost post) {
BlogPost createdPost = blogPostService.createPost(post);
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(createdPost.getId())
.toUri();
return ResponseEntity.created(location).body(createdPost);
}
@PutMapping("/{id}")
public ResponseEntity<BlogPost> updatePost(@PathVariable Long id, @RequestBody BlogPost post) {
return ResponseEntity.ok(blogPostService.updatePost(id, post));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deletePost(@PathVariable Long id) {
blogPostService.deletePost(id);
return ResponseEntity.noContent().build();
}
}
DTO (Data Transfer Object)
import lombok.Value;
import lombok.Builder;
@Value
@Builder
public class BlogPostSummaryDto {
Long id;
String title;
String author;
public static BlogPostSummaryDto fromEntity(BlogPost post) {
return BlogPostSummaryDto.builder()
.id(post.getId())
.title(post.getTitle())
.author(post.getAuthor())
.build();
}
}
Configuration Class
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Slf4j
public class BlogConfig {
@Bean
CommandLineRunner initDatabase(BlogPostRepository repository) {
return args -> {
log.info("Preloading sample data...");
repository.save(BlogPost.builder()
.title("First Post")
.content("This is my first blog post")
.author("John Doe")
.build());
repository.save(BlogPost.builder()
.title("Second Post")
.content("Another interesting post")
.author("Jane Smith")
.build());
};
}
}
Conclusion
Lombok significantly reduces boilerplate code in Spring Boot applications, making your code more concise and readable. By combining Lombok's annotations with Spring's features, you can create clean, maintainable applications with minimal repetitive code.
Remember to:
- Properly configure your IDE for Lombok
- Use the appropriate annotations for each use case
- Be mindful of the generated code (especially with
@Data
and@EqualsAndHashCode
) - Document Lombok usage in your project for team members who may not be familiar with it
With these tools, you can focus more on business logic and less on repetitive Java syntax.