Search code examples
javadecoratormapstruct

MapStruct decorator has no effect generated code


I wrote the needed interface wit annotations and, an abstract class as decorator. After the generation (mvn clean package) the decorated function updated by the "default" process, what come form parameter and result type. I don't know, what is the problem. Could you help me?

Environment: mapstruct version 1.4.2.Final lombok version 1.18.22 (Spring boot 2.6.3) lombok-mapstruct-binding: 0.2.0

        <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${mapstruct.version}</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok-mapstruct-binding</artifactId>
        <version>${lombok-mapstruct-binding.version}</version>
    </dependency>

and

    <build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <excludes>
                    <exclude>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                    </exclude>
                    <exclude>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok-mapstruct-binding</artifactId>
                    </exclude>
                    <exclude>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct</artifactId>
                    </exclude>
                </excludes>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>${lombok.version}</version>
                    </path>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok-mapstruct-binding</artifactId>
                        <version>${lombok-mapstruct-binding.version}</version>
                    </path>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
                <compilerArgs>
                    <compilerArg>-Amapstruct.defaultComponentModel=spring</compilerArg>
                </compilerArgs>
            </configuration>
        </plugin>
    </plugins>
</build>

The Mapper inteface declaration:

@Mapper(componentModel = "spring", uses = {DateMapper.class})
@DecoratedWith(BeerMapperDecorator.class)
public interface BeerMapper
{

    BeerMapper INSTANCE = Mappers.getMapper(BeerMapper.class);

    BeerDto beerToBeerDto(Beer beer);

    Beer beerDtoToBeer(BeerDto beerDto);

    BeerDto beerToBeerDtoWithInventory(Beer beer);

}

The decorator:

public abstract class BeerMapperDecorator implements BeerMapper {

@Autowired
@Qualifier("delegate")
private BeerMapper delegate;

private BeerInventoryService beerInventoryService;

@Autowired
public void setBeerInventoryService(BeerInventoryService beerInventoryService) {
    this.beerInventoryService = beerInventoryService;
}

@Override
public BeerDto beerToBeerDtoWithInventory(Beer beer) {
    BeerDto dto = delegate.beerToBeerDto(beer);
    dto.setQuantityOnHand(beerInventoryService.getOnhandInventory(beer.getId()));

    System.out.println("   -----------  Decorated --------------");

    return dto;
}

}

And the generated source:

    @Override
public BeerDto beerToBeerDtoWithInventory(Beer beer) {
    if ( beer == null ) {
        return null;
    }

    BeerDtoBuilder beerDto = BeerDto.builder();

    beerDto.id( beer.getId() );
    if ( beer.getVersion() != null ) {
        beerDto.version( beer.getVersion().intValue() );
    }
    beerDto.createdDate( dateMapper.asOffsetDateTime( beer.getCreatedDate() ) );
    beerDto.lastModifiedDate( dateMapper.asOffsetDateTime( beer.getLastModifiedDate() ) );
    beerDto.beerName( beer.getBeerName() );
    beerDto.upc( beer.getUpc() );
    beerDto.price( beer.getPrice() );

    return beerDto.build();
}

Solution

  • Check the implementation of the BeerMapperDecorator, this class is marked as the primary injection dependency.

    Should look something like this:

    @Generated(
        value = "org.mapstruct.ap.MappingProcessor",
        date = "2022-03-01T20:17:02+0100",
        comments = "version: 1.5.0.Beta2, compiler: Eclipse JDT (IDE) 1.4.50.v20210914-1429, environment: Java 17.0.1 (Azul Systems, Inc.)"
    )
    @Component
    @Primary
    public class BeerMapperImpl extends BeerMapperDecorator {
    
        @Autowired
        @Qualifier("delegate")
        private BeerMapper delegate;
    
        @Override
        public BeerDto beerToBeerDto(Beer beer)  {
            return delegate.beerToBeerDto( beer );
        }
    
        @Override
        public Beer beerDtoToBeer(BeerDto beerDto)  {
            return delegate.beerDtoToBeer( beerDto );
        }
    }
    

    The one you're looking at is marked as the delegate interface:

    @Component
    @Qualifier("delegate")
    public class BeerMapperImpl_ implements BeerMapper {