Lombok en Spring Boot moderno

Avanzado
Spring Boot
Spring Boot
Actualizado: 07/05/2026

Diagrama: tutorial-spring-boot-lombok-productividad

Por qué Lombok sigue siendo útil en 2026

Lombok es una librería de procesamiento de anotaciones que genera código en tiempo de compilación a partir de anotaciones simples. En Spring Boot moderno con Java 25 LTS, records ya cubren buena parte del caso de uso de inmutabilidad y POJOs de datos, pero hay escenarios concretos donde Lombok sigue siendo la mejor opción:

  • 1. Entidades JPA mutables: las entidades JPA necesitan setters, constructor sin argumentos y equals/hashCode cuidadosos. Records no encajan porque son inmutables y no admiten constructor sin argumentos.
  • 2. Builder fluido: la anotación @Builder genera un patrón builder type-safe que records no proporcionan de forma nativa, útil cuando un objeto tiene muchos parámetros.
  • 3. Logger automático: @Slf4j evita la línea repetitiva private static final Logger log = LoggerFactory.getLogger(...) en cada clase.
  • 4. Constructor injection en servicios: @RequiredArgsConstructor con final campos genera el constructor canónico que Spring usa para inyección.

Definición operativa: Lombok es para clases mutables, builders fluidos y logging. Records son para DTOs, value objects y respuestas inmutables.

Configuración en Spring Boot 4

Para añadir Lombok a un proyecto Spring Boot 4 con Maven se incluye la dependencia con scope = provided (Lombok solo se necesita en compilación):

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <scope>provided</scope>
</dependency>

Spring Boot 4 ya gestiona la versión de Lombok compatible con Java 25 LTS a través de su BOM. En Gradle equivale a:

compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

El plugin del IDE es imprescindible. IntelliJ IDEA lo trae preinstalado desde 2024.1. En VS Code se instala desde el Java Extension Pack. Sin el plugin, el IDE marca errores aunque el código compila bien.

lombok.config

Un archivo lombok.config en la raíz del proyecto define convenciones del equipo:

# Marca código generado para que JaCoCo lo excluya y SonarQube no lo penalice
lombok.addLombokGeneratedAnnotation = true

# Equals usa solo campos marcados explicitamente, no todos
lombok.equalsAndHashCode.callSuper = call

# Para que @Builder mantenga toString consistente
lombok.toString.includeFieldNames = true

# Stop bubbling: este es el archivo raiz, Lombok no busca configs heredadas
config.stopBubbling = true

Estos cuatro ajustes evitan los problemas típicos en producción: cobertura artificial baja por código generado, equals que compara referencias entre entidades JPA en herencia, toString que se rompe entre versiones, y conflictos de configuración entre módulos.

Anotaciones más usadas en Spring Boot

@Data combinado con cuidado en entidades JPA

@Data genera getters, setters, toString, equals y hashCode. Para entidades JPA, su uso debe ser cuidadoso porque equals y hashCode por defecto incluyen todos los campos, lo que rompe colecciones de Hibernate al cargar/guardar. Lo idiomático en JPA es:

@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString(onlyExplicitlyIncluded = true)
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Empleado {

    @Id
    @GeneratedValue
    @ToString.Include
    @EqualsAndHashCode.Include
    private Long id;

    @ToString.Include
    private String nombre;

    private BigDecimal salario;

    @ManyToOne(fetch = FetchType.LAZY)
    private Departamento departamento;
}

Anti-patrón: poner @Data directo en una entidad JPA con asociaciones @OneToMany o @ManyToOne. El toString recorre toda la asociación y dispara consultas LAZY, y equals puede chocar entre entidades aún no persistidas.

@Builder para constructores con muchos parámetros

@Builder genera un builder fluido. Útil para DTOs de respuesta o entidades con muchos campos opcionales:

@Builder
public record EmpleadoFiltro(
    String nombre,
    Long departamentoId,
    BigDecimal salarioMin,
    BigDecimal salarioMax,
    LocalDate altaDesde,
    LocalDate altaHasta
) {}

// Uso
var filtro = EmpleadoFiltro.builder()
    .departamentoId(3L)
    .salarioMin(BigDecimal.valueOf(40000))
    .build();

Sí, @Builder se aplica también sobre records. Lombok genera la clase builder externa y un método estático builder() en el record.

@RequiredArgsConstructor para servicios Spring

Para servicios con campos final inyectados por constructor:

@Service
@RequiredArgsConstructor
@Slf4j
public class EmpleadoService {

    private final EmpleadoRepository repository;
    private final DepartamentoService departamentoService;
    private final ApplicationEventPublisher eventPublisher;

    public EmpleadoDto crear(EmpleadoCrearDto dto) {
        log.info("Creando empleado: nombre={}", dto.nombre());
        // ...
    }
}

Equivale a tres líneas menos por servicio. En proyectos grandes con cientos de servicios, ahorra cientos de líneas de boilerplate sin perder claridad.

@Slf4j para logging

Sustituye:

private static final Logger log = LoggerFactory.getLogger(MiClase.class);

Por:

@Slf4j
public class MiClase {
    public void metodo() {
        log.info("Mensaje");
    }
}

Lombok añade el field log correctamente tipado. Para usar un sistema diferente (p. ej. JBoss Logger) existen @Log, @JBossLog, @CommonsLog. En Spring Boot lo idiomático es @Slf4j.

Combinación idiomática: records para DTOs, Lombok para entidades

En un proyecto Spring Boot 4 con Java 25 LTS, el reparto natural es:

// DTOs de entrada y salida: records (inmutables, automáticamente serializables por Jackson)
public record EmpleadoCrearDto(
    @NotBlank String nombre,
    @NotNull @Min(0) BigDecimal salario,
    @NotNull Long departamentoId
) {}

public record EmpleadoDto(
    Long id,
    String nombre,
    BigDecimal salario,
    DepartamentoDto departamento
) {}

// Entidad JPA: Lombok porque necesita ser mutable
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString(onlyExplicitlyIncluded = true)
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Empleado {
    @Id @GeneratedValue
    @ToString.Include
    @EqualsAndHashCode.Include
    private Long id;

    private String nombre;
    private BigDecimal salario;

    @ManyToOne(fetch = FetchType.LAZY)
    private Departamento departamento;
}

// Servicio: Lombok para constructor + logger
@Service
@RequiredArgsConstructor
@Slf4j
public class EmpleadoService {
    private final EmpleadoRepository repository;
    private final EmpleadoMapper mapper;

    public EmpleadoDto crear(EmpleadoCrearDto dto) {
        log.info("Creando empleado {}", dto.nombre());
        var empleado = mapper.toEntity(dto);
        return mapper.toDto(repository.save(empleado));
    }
}

Esta combinación se mantiene clara: el alumno ve inmediatamente qué clases son inmutables y cuáles mutables, sin necesidad de leer cada anotación.

Integración con MapStruct

MapStruct es la herramienta estándar para mapear entre entidades y DTOs en Spring Boot. La combinación con Lombok es directa:

@Mapper(componentModel = "spring")
public interface EmpleadoMapper {

    EmpleadoDto toDto(Empleado empleado);

    @Mapping(target = "id", ignore = true)
    @Mapping(target = "departamento", source = "departamentoId")
    Empleado toEntity(EmpleadoCrearDto dto);

    default Departamento map(Long departamentoId) {
        if (departamentoId == null) return null;
        var d = new Departamento();
        d.setId(departamentoId);
        return d;
    }
}

Para que el EmpleadoMapper se inyecte como bean Spring (@Component), el componentModel = "spring" es imprescindible. MapStruct procesa las anotaciones después de Lombok, por lo que el orden de los annotation processors es:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <annotationProcessorPaths>
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </path>
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok-mapstruct-binding</artifactId>
                <version>0.2.0</version>
            </path>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

El artifact lombok-mapstruct-binding es necesario para que MapStruct reconozca los métodos generados por Lombok.

Testing con Lombok

Las clases generadas por Lombok son testables como cualquier otra. Hay dos consideraciones:

1. Excluir código generado de la cobertura: con lombok.addLombokGeneratedAnnotation = true en lombok.config, Lombok añade @Generated a métodos generados. JaCoCo lo respeta automáticamente y no penaliza la cobertura.

2. Tests de equals y hashCode: si la entidad tiene @EqualsAndHashCode(onlyExplicitlyIncluded = true) con solo el id incluido, hay que probar que dos instancias con el mismo id son iguales y que dos con id diferente no:

@Test
@DisplayName("dos empleados con mismo id son iguales")
void equals_mismoId_esIgual() {
    var a = Empleado.builder().id(1L).nombre("Ana").build();
    var b = Empleado.builder().id(1L).nombre("Beatriz").build();

    assertThat(a).isEqualTo(b);
    assertThat(a.hashCode()).isEqualTo(b.hashCode());
}

Anti-patrones que ver en proyectos reales

@Data en entidades JPA con asociaciones

Genera toString que recorre asociaciones LAZY y dispara queries. Genera equals basado en todos los campos, lo que rompe Set<Empleado> cuando una entidad cambia de transient a persistent y su id pasa de null a un valor. Usa @Getter, @Setter y @EqualsAndHashCode/@ToString con onlyExplicitlyIncluded = true.

Field injection con @Autowired junto a Lombok

// Mal
@Service
public class MiServicio {
    @Autowired
    private OtroServicio otroServicio;
}

// Bien
@Service
@RequiredArgsConstructor
public class MiServicio {
    private final OtroServicio otroServicio;
}

La inyección por constructor con final es la convención moderna desde Spring 4.3 y Lombok la simplifica a una anotación.

@Value en lugar de records

// Anti-patrón en Java 25
@Value
public class Coordenada {
    BigDecimal lat;
    BigDecimal lng;
}

// Idiomático
public record Coordenada(BigDecimal lat, BigDecimal lng) {}

@Value es la versión inmutable de @Data. Antes de records era útil; con Java 14+ los records lo sustituyen con sintaxis nativa, mejor integración con pattern matching y deconstrucción, y sin necesidad de Lombok en clases de datos.

Regla práctica: @Value está deprecado de facto en proyectos Java 21+. Si necesitas inmutabilidad, usa records.

@Builder en records sin sentido

@Builder en un record con dos o tres campos no aporta nada (el constructor canónico es igual de claro):

// Innecesario
@Builder
public record Punto(int x, int y) {}

// Mejor
public record Punto(int x, int y) {}
// Usar: new Punto(3, 4) directamente

Reserva @Builder para records con más de 4-5 campos, varios opcionales, o cuando hay parámetros del mismo tipo (un builder hace el código más legible que new Foo(1, 2, 3, 4, 5, 6)).

Lombok y Java records: resumen de decisión

| Caso | Recomendación | |------|---------------| | DTO de respuesta REST | record | | DTO de entrada con validación | record con anotaciones de validación | | Filtro de búsqueda con muchos parámetros opcionales | record con @Builder | | Value object inmutable (Money, Coordenada, EmailAddress) | record | | Entidad JPA mutable | clase con Lombok (@Getter, @Setter, @NoArgsConstructor, @EqualsAndHashCode cuidadoso) | | Servicio Spring con dependencias | clase con @RequiredArgsConstructor + final campos | | Logger | @Slf4j siempre | | Builder de muchos campos | @Builder (sobre clase o record) |

La regla general es: records cuando la inmutabilidad encaja, Lombok cuando hace falta mutabilidad o boilerplate de equipo. Y siempre que sea posible, mantener un estilo consistente en el módulo para que el lector no tenga que cambiar de mentalidad cada cinco líneas.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Spring Boot es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Spring Boot

Explora más contenido relacionado con Spring Boot y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Eliminar boilerplate en entidades JPA, DTOs y servicios con Lombok. Decidir cuándo usar Lombok y cuándo records de Java 25. Integrar Lombok con MapStruct, Jackson y testing. Configurar lombok.config para alinear estilo en el equipo.