Let's get started with a Microservice Architecture with Spring Cloud:
Using MapStruct With Lombok
Last updated: March 13, 2025
1. Overview
Project Lombok is a library that helps with boilerplate code in Java, allowing us to focus more on the core application logic. Similarly, MapStruct is another library that reduces boilerplate when mapping between two Java beans.
Because these both use annotation processors, an advanced Java feature, it’s easy to make mistakes when getting the project set up.
In this tutorial, we’ll cover how to correctly configure Maven and then show at the end a working project.
2. Classpath
2.1. Dependencies
Let’s add the mapstruct and lombok dependencies to our project’s pom.xml file:
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.6.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
<scope>provided</scope>
</dependency>
</dependencies>
The provided dependency scope for the lombok dependencies means that they are used only at compile time and shouldn’t end up on the runtime classpath.
2.2. Annotation Processors
We also need to configure the annotation processor path with both mapstruct-processor and lombok-mapstruct-binding in a maven-compiler-plugin declaration:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.6.3</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
It’s easy to make a mistake here, as annotation processors are an advanced feature. The main mistake is to forget that our runtime will search either the annotation processor path or the classpath for annotation processors, but it won’t search both.
We should note that for Lombok version 1.18.16 and above, we need to add both the Lombok lombok-mapstruct-binding and the mapstruct-processor dependencies to the annotationProcessorPaths element. When we don’t do this, we could get a compilation error: “Unknown Property in Result Type…”.
We need the lombok-mapstruct-binding dependency in order to make Lombok and MapStruct work together. Essentially, it tells MapStruct to wait until Lombok has done all its annotation processing before generating mapper classes for Lombok-enhanced beans.
3. MapStruct and Lombok Integration
We’ll use the @Builder and @Data Lombok annotations in our setup. While the former allows object creation via the Builder pattern, the latter provides constructor-based object creation via setters.
3.1. Java POJO Setup
Now, let’s start by defining a simple source class for our mapper:
@Data
public class SimpleSource {
private String name;
private String description;
}
Next, we define a simple destination class for our mapper:
@Data
public class SimpleDestination {
private String name;
private String description;
}
Finally, we’ll also define another destination class, but using the @Builder Lombok annotation:
@Builder
@Getter
public class LombokDestination {
private String name;
private String description;
}
3.2. Using the @Mapper Annotation
When we use the @Mapper annotation, MapStruct automatically creates the mapper implementation.
Let’s define the mapper interface:
@Mapper
public interface LombokMapper {
SimpleDestination sourceToDestination(SimpleSource source);
LombokDestination sourceToLombokDestination(SimpleSource source);
}
When we execute the mvn clean install command, the mapper implementation class is created under the /target/generated-sources/annotations/ folder.
Let’s go through the generated implementation class:
public class LombokMapperImpl implements LombokMapper {
@Override
public SimpleDestination sourceToDestination(SimpleSource source) {
if ( source == null ) {
return null;
}
SimpleDestination simpleDestination = new SimpleDestination();
simpleDestination.setName( source.getName() );
simpleDestination.setDescription( source.getDescription() );
return simpleDestination;
}
@Override
public LombokDestination sourceToLombokDestination(SimpleSource source) {
if ( source == null ) {
return null;
}
LombokDestination.LombokDestinationBuilder lombokDestination = LombokDestination.builder();
lombokDestination.name( source.getName() );
lombokDestination.description( source.getDescription() );
return lombokDestination.build();
}
}
As we can see here, the implementation has two methods for mapping the source object to the different destinations. However, the major difference lies in how the destination object is built.
The implementation uses the builder() method for the LombokDestination class. On the other hand, it uses the constructor to create the SimpleDestination object and the setters to map the variables.
3.3. Test Case
Now, let’s look at a simple test case to see the mapper implementation in action:
@Test
void whenDestinationIsMapped_thenIsSuccessful() {
SimpleSource simpleSource = new SimpleSource();
simpleSource.setName("file");
simpleSource.setDescription("A text file.");
SimpleDestination simpleDestination = lombokMapper.sourceToDestination(simpleSource);
Assertions.assertNotNull(simpleDestination);
Assertions.assertEquals(simpleSource.getName(), simpleDestination.getName());
Assertions.assertEquals(simpleSource.getDescription(), simpleDestination.getDescription());
LombokDestination lombokDestination = lombokMapper.sourceToLombokDestination(simpleSource);
Assertions.assertNotNull(lombokDestination);
Assertions.assertEquals(simpleSource.getName(), lombokDestination.getName());
Assertions.assertEquals(simpleSource.getDescription(), lombokDestination.getDescription());
}
As we can verify in the test case above, the mapper implementation successfully maps the source POJO to both the destination POJOs.
4. Conclusion
In this article, we looked at how using MapStruct and Lombok together can help us write less boilerplate, which enhances the readability of the code and increases the efficiency of the development process.
We looked at how to use the @Builder and the @Data Lombok annotations with MapStruct while mapping from one POJO to another.
The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.

















