Skip to content

@AfterMapping methods are called twice when target uses Lombok @Builder annotation #3678

Description

@rainer-wei

Expected behavior

Generated code calls @AfterMapping method only once. Here is the generated output for MapStruct 1.5.5:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2024-08-19T16:21:29+0200",
    comments = "version: 1.5.5.Final, compiler: javac, environment: Java 21.0.2 (Oracle Corporation)"
)
class MappingExample$SimpleSourceDestinationMapperImpl extends MappingExample.SimpleSourceDestinationMapper {

    @Override
    MappingExample.SimpleDestination sourceToDestination(MappingExample.SimpleSource source) {
        if ( source == null ) {
            return null;
        }

        MappingExample.SimpleDestination.SimpleDestinationBuilder simpleDestination = MappingExample.SimpleDestination.builder();

        simpleDestination.name( source.getName() );
        simpleDestination.description( source.getDescription() );

        afterMapping( source );

        return simpleDestination.build();
    }
}

Actual behavior

@AfterMapping method is called before and after using the build() method of the builder pattern.

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2024-08-19T16:24:57+0200",
    comments = "version: 1.6.0, compiler: javac, environment: Java 21.0.2 (Oracle Corporation)"
)
class MappingExample$SimpleSourceDestinationMapperImpl extends MappingExample.SimpleSourceDestinationMapper {

    @Override
    MappingExample.SimpleDestination sourceToDestination(MappingExample.SimpleSource source) {
        if ( source == null ) {
            return null;
        }

        MappingExample.SimpleDestination.SimpleDestinationBuilder simpleDestination = MappingExample.SimpleDestination.builder();

        simpleDestination.name( source.getName() );
        simpleDestination.description( source.getDescription() );

        afterMapping( source );

        SimpleDestination simpleDestinationResult = simpleDestination.build();

        afterMapping( source );

        return simpleDestinationResult;
    }
}

Steps to reproduce the problem

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>test-mapstruct-issue</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>1.6.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.34</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok-mapstruct-binding</artifactId>
            <version>0.2.0</version>
        </dependency>
    </dependencies>

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

Code example:

import lombok.Builder;
import lombok.Data;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;

public class MappingExample {

  @Data
  public static class SimpleSource {
    private String name;
    private String description;
  }

  @Data
  @Builder
  public static final class SimpleDestination {
    private final String name;
    private final String description;

  }

  @Mapper
  abstract static class SimpleSourceDestinationMapper {

    abstract SimpleDestination sourceToDestination(SimpleSource source);

    @AfterMapping
    void afterMapping(SimpleSource simpleSource) {
      // do something
    }
  }
}

MapStruct Version

MapStruct 1.6.0

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions