Project Lombok
Eliminating Boilerplate Code and Annotations in Spring Boot
Project Lombok is a compile-time annotation processing library that automates the generation of repetitive boilerplate code in Java applications, such as getters, setters, constructors, toString(), equals(), hashCode(), and loggers.
1. What is Lombok and how does it work?
During standard Java compilation, writing private attributes requires defining dozens of lines of redundant code. Lombok hooks into the compile-time annotation processing phase of javac and directly injects bytecode for methods into compiled .class files, keeping .java source files completely clean.
2. Installation and Setup
To use Lombok in a Spring Boot project, declare the dependency in your build tool:
- Maven (pom.xml)
- Gradle (build.gradle)
<dependencies>
<!-- Project Lombok dependency -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<!-- Excludes Lombok from final executable JAR since it is only needed at compile time -->
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
dependencies {
// Lombok as compile-only dependency and annotation processor
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}
After declaring the Lombok dependency and annotation processor, verify that the library downloads and configures correctly on your classpath by running:
- Maven:
./mvnw clean install(ormvn clean install) - Gradle:
./gradlew build(or./gradlew --refresh-dependencies)
This command processes annotations and confirms that javac recognizes Lombok instructions without errors.
3. Detailed Lombok Annotation Catalog
3.1. Encapsulation (@Getter and @Setter)
Generates read (getFields()) and write (setFields()) methods. Can be placed at the class level or on individual fields.
package com.icesi.app.model;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Product {
private Long id;
private String name;
// Overrides setter visibility to private for this specific field
@Setter(lombok.AccessLevel.PRIVATE)
private Double price;
}
3.2. Constructor Generation
@NoArgsConstructor: Generates a no-argument constructor (required by frameworks like JPA and Jackson).@AllArgsConstructor: Generates a constructor with one parameter for every field in the class.@RequiredArgsConstructor: Generates a constructor only for fields marked asfinalor annotated with@NonNull. This is the recommended Spring pattern for constructor dependency injection.
package com.icesi.app.service.impl;
import com.icesi.app.repository.ProductRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
// Automatically generates a constructor for 'productRepository' because it is declared 'final'
@RequiredArgsConstructor
public class ProductServiceImpl {
// Spring automatically injects this mandatory dependency without writing manual constructor code
private final ProductRepository productRepository;
}
3.3. Code Bundlers (@Data and @Value)
@Data: Bundles@Getter,@Setter,@ToString,@EqualsAndHashCode, and@RequiredArgsConstructor. Ideal for simple DTOs or POJOs.@Value: Immutable variant of@Data. Makes all fieldsprivate finaland generates no setters.
package com.icesi.app.dto;
import lombok.Data;
@Data
public class UserResponseDto {
private Long id;
private String name;
private String email;
}
3.4. Design Patterns & Logging (@Builder and @Slf4j)
@Builder: Produces a fluent builder API based on the Builder design pattern.@Slf4j: Automatically injects an SLF4J logger instance namedlog.
package com.icesi.app.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class NotificationService {
public void sendEmail(String to, String subject) {
log.info("Sending email to: {} with subject: {}", to, subject);
log.debug("Technical payload details...");
}
}
4. Integrated Example: Builder Pattern
package com.icesi.app.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Customer {
private Long id;
private String firstName;
private String lastName;
private String email;
private Boolean active;
}
Fluent Builder Usage:
Customer customer = Customer.builder()
.id(1L)
.firstName("Kevin")
.lastName("Coes")
.email("kevin@icesi.edu.co")
.active(true)
.build();
5. Warnings and Best Practices with JPA and Lombok
Avoid placing @Data on classes annotated with JPA @Entity if they contain bidirectional relational relationships (@OneToMany / @ManyToOne).
Why?
@Data automatically generates hashCode() and equals() using all fields. If two entities reference each other bidirectionally, executing hashCode() triggers circular invocation leading to a StackOverflowError.
Recommendation:
Use explicit annotations on JPA entities:
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class User { ... }