Coder Social home page Coder Social logo

mapstruct-spring-extensions's Introduction

MapStruct - Java bean mappings, the easy way!

Latest Stable Version Latest Version License

Build Status Coverage Status Gitter Code Quality: Java Total Alerts

What is MapStruct?

MapStruct is a Java annotation processor for the generation of type-safe and performant mappers for Java bean classes. It saves you from writing mapping code by hand, which is a tedious and error-prone task. The generator comes with sensible defaults and many built-in type conversions, but it steps out of your way when it comes to configuring or implementing special behavior.

Compared to mapping frameworks working at runtime, MapStruct offers the following advantages:

  • Fast execution by using plain method invocations instead of reflection
  • Compile-time type safety. Only objects and attributes mapping to each other can be mapped, so there's no accidental mapping of an order entity into a customer DTO, etc.
  • Self-contained code—no runtime dependencies
  • Clear error reports at build time if:
    • mappings are incomplete (not all target properties are mapped)
    • mappings are incorrect (cannot find a proper mapping method or type conversion)
  • Easily debuggable mapping code (or editable by hand—e.g. in case of a bug in the generator)

To create a mapping between two types, declare a mapper interface like this:

@Mapper
public interface CarMapper {

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

    @Mapping(target = "seatCount", source = "numberOfSeats")
    CarDto carToCarDto(Car car);
}

At compile time MapStruct will generate an implementation of this interface. The generated implementation uses plain Java method invocations for mapping between source and target objects, i.e. no reflection is involved. By default, properties are mapped if they have the same name in source and target, but you can control this and many other aspects using @Mapping and a handful of other annotations.

Requirements

MapStruct requires Java 1.8 or later.

Using MapStruct

MapStruct works in command line builds (plain javac, via Maven, Gradle, Ant, etc.) and IDEs.

For Eclipse, a dedicated plug-in is in development (see https://github.com/mapstruct/mapstruct-eclipse). It goes beyond what's possible with an annotation processor, providing content assist for annotation attributes, quick fixes and more.

For IntelliJ the plug-in is available within the IntelliJ marketplace (see https://plugins.jetbrains.com/plugin/10036-mapstruct-support).

Maven

For Maven-based projects, add the following to your POM file in order to use MapStruct (the dependencies are available at Maven Central):

...
<properties>
    <org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
</properties>
...
<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>
...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>
...

Gradle

For Gradle, you need something along the following lines:

plugins {
    ...
    id "com.diffplug.eclipse.apt" version "3.26.0" // Only for Eclipse
}

dependencies {
    ...
    implementation 'org.mapstruct:mapstruct:1.5.5.Final'

    annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
    testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final' // if you are using mapstruct in test code
}
...

If you don't work with a dependency management tool, you can obtain a distribution bundle from Releases page.

Documentation and getting help

To learn more about MapStruct, refer to the project homepage. The reference documentation covers all provided functionality in detail. If you need help please ask it in the Discussions.

Building from Source

MapStruct uses Maven for its build. Java 11 is required for building MapStruct from source. To build the complete project, run

./mvnw clean install

from the root of the project directory. To skip the distribution module, run

./mvnw clean install -DskipDistribution=true

Importing into IDE

MapStruct uses the gem annotation processor to generate mapping gems for its own annotations. Therefore, for seamless integration within an IDE annotation processing needs to be enabled.

IntelliJ

Make sure that you have at least IntelliJ 2018.2.x (needed since support for annotationProcessors from the maven-compiler-plugin is from that version). Enable annotation processing in IntelliJ (Build, Execution, Deployment -> Compiler -> Annotation Processors)

Eclipse

Make sure that you have the m2e_apt plugin installed.

Links

Licensing

MapStruct is licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0.

mapstruct-spring-extensions's People

Contributors

chessray avatar david-hamilton-bah avatar dmham86 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mapstruct-spring-extensions's Issues

Adding @Context is creating two methods, how to solve this using ConversionService ?

When Integrating with @Context in @AfterMapping or @BeforeMapping is not working

Below Example is working as expected,

@Mapper(
        config = MapperSpringConfig.class,
        builder = @Builder(disableBuilder = true),
        uses = EntityManager.class)
public interface NewPostRequestToPostEntityMapper extends Converter<NewPostRequest, PostEntity> {

    @Mapping(target = "tags", ignore = true)
    PostEntity convert(NewPostRequest newPostRequest);

    @AfterMapping
    default void afterMapping(NewPostRequest newPostRequest, @MappingTarget PostEntity postEntity) {
        newPostRequest
                .tags()
                .forEach(tagsRequest -> postEntity.addTag(getTagEntity(null, tagsRequest)));
    }
}

But the moment I add @Context EntityManager entityManager as third parameter afterMapping method is not generating. I expect it to be working like normal Mapstruct.

@Mapper(
        config = MapperSpringConfig.class,
        builder = @Builder(disableBuilder = true),
        uses = EntityManager.class)
public interface NewPostRequestToPostEntityMapper extends Converter<NewPostRequest, PostEntity> {

    @Mapping(target = "tags", ignore = true)
    PostEntity convert(NewPostRequest newPostRequest);

    @AfterMapping
    default void afterMapping(
            NewPostRequest newPostRequest,
            @MappingTarget PostEntity postEntity,
            @Context EntityManager entityManager) {
        newPostRequest
                .tags()
                .forEach(
                        tagsRequest -> postEntity.addTag(getTagEntity(entityManager, tagsRequest)));
    }
}

Sample code can be found here to reproduce the issue

maven Compilation failed [JDK20 MAVEN3.9.2 ] javax.annotation.processing.Processor: Provider org.mapstruct.extensions.spring.converter.ConverterMapperProcessor could not be instantiated

environment

Java and Maven information

C:Usersqolom>java -version
java version "20.0.1" 2023-04-18
Java(TM) SE Runtime Environment (build 20.0.1+9-29)
Java HotSpot(TM) 64-Bit Server VM (build 20.0.1+9-29, mixed mode, sharing)

C:Usersqolom>mvn -v
Apache Maven 3.9.2 (c9616018c7a021c1c39be70fb2843d6f5f9b8a1c)
Maven home: D:Serverapache-maven-3.9.2
Java version: 20.0.1, vendor: Oracle Corporation, runtime: C:Program FilesJavajdk-20
Default locale: zh_CN, platform encoding: UTF-8
OS name: "windows 11", version: "10.0", arch: "amd64", family: "windows"

pom information

<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>


     <parent>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent -->
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.0</version>
        <relativePath/>
        <!-- lookup parent from repository -->
    </parent>




    <properties>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <lombok.version>1.18.28</lombok.version>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok-mapstruct-binding -->
        <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>

        <!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct -->
        <mapstruct.version>1.5.5.Final</mapstruct.version>

        <!-- https://mvnrepository.com/artifact/org.mapstruct.extensions.spring/mapstruct-spring-annotations -->
        <mapstruct-spring-annotations.version>1.0.1</mapstruct-spring-annotations.version>

        <!-- https://mvnrepository.com/artifact/org.mapstruct.extensions.spring/mapstruct-spring-extensions -->
        <mapstruct-spring-extensions.version>1.0.1</mapstruct-spring-extensions.version>


          <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
        <spring-cloud-dependencies.version>2022.0.3</spring-cloud-dependencies.version>

    </properties>

<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${mapstruct.version}</version>
    </dependency>
    <dependency>
        <groupId>org.mapstruct.extensions.spring</groupId>
        <artifactId>mapstruct-spring-extensions</artifactId>
        <version>${mapstruct-spring-extensions.version}</version>
    </dependency>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
         <version>${mapstruct.version}</version>
    </dependency>
    <dependency>
        <groupId>org.mapstruct.extensions.spring</groupId>
        <artifactId>mapstruct-spring-annotations</artifactId>
        <version>${mapstruct-spring-annotations.version}</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
         <version>${lombok.version}</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok-mapstruct-binding</artifactId>
        <version>${lombok-mapstruct-binding.version}</version>
    </dependency>


    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-consul-all</artifactId>
    </dependency>
    <dependency>
        <groupId>net.logstash.logback</groupId>
        <artifactId>logstash-logback-encoder</artifactId>
    </dependency>


    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
    </dependency>


    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-core</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-common -->
    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-common</artifactId>
    </dependency>

</dependencies>




</project>






Related logs

"C:\Program Files\Java\jdk-20\bin\java.exe" -Dvisualvm.id=8147785106100 -Dmaven.multiModuleProjectDirectory=D:\workspace\cloud-workspace\cloud-common -Djansi.passthrough=true -Xms512m -Xmx1024m -Dfile.encoding=UTF-8 -Dmaven.home=D:\Server\apache-maven-3.9.2 -Dclassworlds.conf=D:\Server\apache-maven-3.9.2\bin\m2.conf "-Dmaven.ext.class.path=D:\Software\IntelliJ IDEA 2023.1.2\plugins\maven\lib\maven-event-listener.jar" "-javaagent:D:\Software\IntelliJ IDEA 2023.1.2\lib\idea_rt.jar=58531:D:\Software\IntelliJ IDEA 2023.1.2\bin" -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath D:\Server\apache-maven-3.9.2\boot\plexus-classworlds-2.7.0.jar;D:\Server\apache-maven-3.9.2\boot\plexus-classworlds.license org.codehaus.classworlds.Launcher -Didea.version=2023.1.2 --update-snapshots -s D:\Server\apache-maven-3.9.2\conf\settings.xml clean package install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] cloud-common                                                       [pom]
[INFO] cloud-common-client                                                [jar]
[INFO] cloud-common-server                                                [jar]
[INFO] 
[INFO] -------------------< com.qolome.cloud:cloud-common >--------------------
[INFO] Building cloud-common 2.1.3-jre20                                  [1/3]
[INFO]   from pom.xml
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] --- clean:3.2.0:clean (default-clean) @ cloud-common ---
[INFO] 
[INFO] >>> source:3.2.1:jar (default) > generate-sources @ cloud-common >>>
[INFO] 
[INFO] <<< source:3.2.1:jar (default) < generate-sources @ cloud-common <<<
[INFO] 
[INFO] 
[INFO] --- source:3.2.1:jar (default) @ cloud-common ---
[INFO] 
[INFO] --- javadoc:3.5.0:jar (attach-javadocs) @ cloud-common ---
[INFO] Not executing Javadoc as the project is not a Java classpath-capable package
[INFO] 
[INFO] >>> source:3.2.1:jar (default) > generate-sources @ cloud-common >>>
[INFO] 
[INFO] <<< source:3.2.1:jar (default) < generate-sources @ cloud-common <<<
[INFO] 
[INFO] 
[INFO] --- source:3.2.1:jar (default) @ cloud-common ---
[INFO] 
[INFO] --- javadoc:3.5.0:jar (attach-javadocs) @ cloud-common ---
[INFO] Not executing Javadoc as the project is not a Java classpath-capable package
[INFO] 
[INFO] --- install:3.1.1:install (default-install) @ cloud-common ---
[INFO] Installing D:\workspace\cloud-workspace\cloud-common\pom.xml to D:\Server\Repository\com\qolome\cloud\cloud-common\2.1.3-jre20\cloud-common-2.1.3-jre20.pom
[INFO] 
[INFO] ----------------< com.qolome.cloud:cloud-common-client >----------------
[INFO] Building cloud-common-client 2.1.3-jre20                           [2/3]
[INFO]   from cloud-common-client\pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- clean:3.2.0:clean (default-clean) @ cloud-common-client ---
[INFO] Deleting D:\workspace\cloud-workspace\cloud-common\cloud-common-client\target
[INFO] 
[INFO] --- resources:3.3.1:resources (default-resources) @ cloud-common-client ---
[INFO] skip non existing resourceDirectory D:\workspace\cloud-workspace\cloud-common\cloud-common-client\src\main\resources
[INFO] skip non existing resourceDirectory D:\workspace\cloud-workspace\cloud-common\cloud-common-client\src\main\resources
[INFO] 
[INFO] --- compiler:3.11.0:compile (default-compile) @ cloud-common-client ---
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 25 source files with javac [debug release 20] to target\classes
[INFO] 
[INFO] >>> source:3.2.1:jar (default) > generate-sources @ cloud-common-client >>>
[INFO] 
[INFO] <<< source:3.2.1:jar (default) < generate-sources @ cloud-common-client <<<
[INFO] 
[INFO] 
[INFO] --- source:3.2.1:jar (default) @ cloud-common-client ---
[INFO] Building jar: D:\workspace\cloud-workspace\cloud-common\cloud-common-client\target\cloud-common-client-2.1.3-jre20-sources.jar
[INFO] 
[INFO] --- resources:3.3.1:testResources (default-testResources) @ cloud-common-client ---
[INFO] skip non existing resourceDirectory D:\workspace\cloud-workspace\cloud-common\cloud-common-client\src\test\resources
[INFO] 
[INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ cloud-common-client ---
[INFO] No sources to compile
[INFO] 
[INFO] --- surefire:3.0.0:test (default-test) @ cloud-common-client ---
[INFO] No tests to run.
[INFO] 
[INFO] --- jar:3.3.0:jar (default-jar) @ cloud-common-client ---
[INFO] Building jar: D:\workspace\cloud-workspace\cloud-common\cloud-common-client\target\cloud-common-client-2.1.3-jre20.jar
[INFO] 
[INFO] --- javadoc:3.5.0:jar (attach-javadocs) @ cloud-common-client ---
[INFO] No previous run data found, generating javadoc.
[INFO] Building jar: D:\workspace\cloud-workspace\cloud-common\cloud-common-client\target\cloud-common-client-2.1.3-jre20-javadoc.jar
[INFO] 
[INFO] --- resources:3.3.1:resources (default-resources) @ cloud-common-client ---
[INFO] skip non existing resourceDirectory D:\workspace\cloud-workspace\cloud-common\cloud-common-client\src\main\resources
[INFO] skip non existing resourceDirectory D:\workspace\cloud-workspace\cloud-common\cloud-common-client\src\main\resources
[INFO] 
[INFO] --- compiler:3.11.0:compile (default-compile) @ cloud-common-client ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] >>> source:3.2.1:jar (default) > generate-sources @ cloud-common-client >>>
[INFO] 
[INFO] <<< source:3.2.1:jar (default) < generate-sources @ cloud-common-client <<<
[INFO] 
[INFO] 
[INFO] --- source:3.2.1:jar (default) @ cloud-common-client ---
[WARNING] artifact com.qolome.cloud:cloud-common-client:java-source:sources:2.1.3-jre20 already attached, replace previous instance
[INFO] 
[INFO] --- resources:3.3.1:testResources (default-testResources) @ cloud-common-client ---
[INFO] skip non existing resourceDirectory D:\workspace\cloud-workspace\cloud-common\cloud-common-client\src\test\resources
[INFO] 
[INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ cloud-common-client ---
[INFO] No sources to compile
[INFO] 
[INFO] --- surefire:3.0.0:test (default-test) @ cloud-common-client ---
[INFO] No tests to run.
[INFO] Skipping execution of surefire because it has already been run for this configuration
[INFO] 
[INFO] --- jar:3.3.0:jar (default-jar) @ cloud-common-client ---
[INFO] 
[INFO] --- javadoc:3.5.0:jar (attach-javadocs) @ cloud-common-client ---
[INFO] Configuration changed, re-generating javadoc.
[INFO] Building jar: D:\workspace\cloud-workspace\cloud-common\cloud-common-client\target\cloud-common-client-2.1.3-jre20-javadoc.jar
[WARNING] artifact com.qolome.cloud:cloud-common-client:javadoc:javadoc:2.1.3-jre20 already attached, replace previous instance
[INFO] 
[INFO] --- install:3.1.1:install (default-install) @ cloud-common-client ---
[INFO] Installing D:\workspace\cloud-workspace\cloud-common\cloud-common-client\pom.xml to D:\Server\Repository\com\qolome\cloud\cloud-common-client\2.1.3-jre20\cloud-common-client-2.1.3-jre20.pom
[INFO] Installing D:\workspace\cloud-workspace\cloud-common\cloud-common-client\target\cloud-common-client-2.1.3-jre20.jar to D:\Server\Repository\com\qolome\cloud\cloud-common-client\2.1.3-jre20\cloud-common-client-2.1.3-jre20.jar
[INFO] Installing D:\workspace\cloud-workspace\cloud-common\cloud-common-client\target\cloud-common-client-2.1.3-jre20-sources.jar to D:\Server\Repository\com\qolome\cloud\cloud-common-client\2.1.3-jre20\cloud-common-client-2.1.3-jre20-sources.jar
[INFO] Installing D:\workspace\cloud-workspace\cloud-common\cloud-common-client\target\cloud-common-client-2.1.3-jre20-javadoc.jar to D:\Server\Repository\com\qolome\cloud\cloud-common-client\2.1.3-jre20\cloud-common-client-2.1.3-jre20-javadoc.jar
[INFO] 
[INFO] ----------------< com.qolome.cloud:cloud-common-server >----------------
[INFO] Building cloud-common-server 2.1.3-jre20                           [3/3]
[INFO]   from cloud-common-server\pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- clean:3.2.0:clean (default-clean) @ cloud-common-server ---
[INFO] Deleting D:\workspace\cloud-workspace\cloud-common\cloud-common-server\target
[INFO] 
[INFO] --- resources:3.3.1:resources (default-resources) @ cloud-common-server ---
[INFO] Copying 0 resource from src\main\resources to target\classes
[INFO] Copying 1 resource from src\main\resources to target\classes
[INFO] 
[INFO] --- compiler:3.11.0:compile (default-compile) @ cloud-common-server ---
[INFO] Changes detected - recompiling the module! :dependency
[INFO] Compiling 9 source files with javac [debug release 20] to target\classes
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR : 
[INFO] -------------------------------------------------------------
[ERROR] 服务配置文件不正确, 或构造处理程序对象javax.annotation.processing.Processor: Provider org.mapstruct.extensions.spring.converter.ConverterMapperProcessor could not be instantiated时抛出异常错误
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for cloud-common 2.1.3-jre20:
[INFO] 
[INFO] cloud-common ....................................... SUCCESS [  0.663 s]
[INFO] cloud-common-client ................................ SUCCESS [  4.449 s]
[INFO] cloud-common-server ................................ FAILURE [  0.586 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  5.914 s
[INFO] Finished at: 2023-06-17T16:27:29+08:00
[INFO] ------------------------------------------------------------------------
[WARNING] 
[WARNING] Plugin validation issues were detected in 2 plugin(s)
[WARNING] 
[WARNING]  * org.apache.maven.plugins:maven-source-plugin:3.2.1
[WARNING]  * org.apache.maven.plugins:maven-javadoc-plugin:3.5.0
[WARNING] 
[WARNING] For more or less details, use 'maven.plugin.validation' property with one of the values (case insensitive): [BRIEF, DEFAULT, VERBOSE]
[WARNING] 
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile (default-compile) on project cloud-common-server: Compilation failure
[ERROR] 服务配置文件不正确, 或构造处理程序对象javax.annotation.processing.Processor: Provider org.mapstruct.extensions.spring.converter.ConverterMapperProcessor could not be instantiated时抛出异常错误
[ERROR] 
[ERROR] -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
[ERROR] 
[ERROR] After correcting the problems, you can resume the build with the command
[ERROR]   mvn <args> -rf :cloud-common-server

Process finished with exit code 1

Problem of defining parent-child JPA mapping.

In my current project I've started to use mapstruts together with mapstruct-spring-extension and when I've started to save jpa objects generated by convertes I've realized that dependent object in relations like one-to-many are not saved. Reason for that is that child object doesn't contain reference to it's parent. Solution for this issue is provided by mapstructs and described in one of examples. I've struggled to achieve same behavior in together with spring-extension, but without success.
Mappers interfaces needs to extend Converter interface from spring, where there is one argument method convert and in order to achieve effect from https://github.com/mapstruct/mapstruct-examples/tree/main/mapstruct-jpa-child-parent somehow we need to pass @context param. Is there any way of solving this problem ?

ClassCastException upon compilation

Hi,

I am eager to move my mappers into the Conversion Service and have found an issue. it is related to an issue I have raised with the mapstruct project (mapstruct/mapstruct#2352).

I get the following exception

Caused by: java.lang.ClassCastException: com.squareup.javapoet.ParameterizedTypeName cannot be cast to com.squareup.javapoet.ClassName
    at org.mapstruct.extensions.spring.converter.ConverterMapperProcessor.toFromToMapping (ConverterMapperProcessor.java:100)
    at java.util.stream.ReferencePipeline$3$1.accept (ReferencePipeline.java:193)
    at java.util.stream.ReferencePipeline$3$1.accept (ReferencePipeline.java:193)
    at java.util.stream.ReferencePipeline$2$1.accept (ReferencePipeline.java:175)
    at java.util.stream.ReferencePipeline$3$1.accept (ReferencePipeline.java:193)
    at java.util.stream.ReferencePipeline$2$1.accept (ReferencePipeline.java:175)
    at java.util.stream.ReferencePipeline$2$1.accept (ReferencePipeline.java:175)
    at java.util.Iterator.forEachRemaining (Iterator.java:116)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining (Spliterators.java:1801)
    at java.util.stream.AbstractPipeline.copyInto (AbstractPipeline.java:482)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:472)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential (ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate (AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect (ReferencePipeline.java:499)
    at org.mapstruct.extensions.spring.converter.ConverterMapperProcessor.processMapperAnnotation (ConverterMapperProcessor.java:87)
    at org.mapstruct.extensions.spring.converter.ConverterMapperProcessor.lambda$process$1 (ConverterMapperProcessor.java:70)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.accept (ForEachOps.java:184)
    at java.util.stream.ReferencePipeline$2$1.accept (ReferencePipeline.java:175)
    at java.util.Iterator.forEachRemaining (Iterator.java:116)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining (Spliterators.java:1801)
    at java.util.stream.AbstractPipeline.copyInto (AbstractPipeline.java:482)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:472)
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential (ForEachOps.java:151)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential (ForEachOps.java:174)
    at java.util.stream.AbstractPipeline.evaluate (AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.forEach (ReferencePipeline.java:418)
    at org.mapstruct.extensions.spring.converter.ConverterMapperProcessor.process (ConverterMapperProcessor.java:68)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor (JavacProcessingEnvironment.java:794)

The set up I have is the following...

@Value
public class TheDto {
    @NonNull
    String id;
}

@Value
public class TheModel {
    @NonNull
    private final String id;
}

public class TheModels extends ArrayList<TheModel> {
    private static final long serialVersionUID = 1L;
}

@Mapper(uses = TheModelMapper.class, imports = TheModel.class)
@SpringMapperConfig
public interface TheModelsMapper extends Converter<TheModels, List<TheDto>> {

    @Override
    List<TheDto> convert(TheModels theModels);

}

@Mapper
public interface TheModelMapper  {
    TheDto convert(TheModel theModel);
}

The issue is the class extending the ArrayList. My other modules have all worked without issue (though interaction with Eclipse (STS) is not quite there yet - tends to build the default ConversionAdapter alot instead of the renamed one. Have to clean and rebuild all for it to be correct - But I can live with that :) )

Getting an error on build but building is successfull anyway

I configured the mapstruct spring extension according the guide, but when doing a mvn package I get following, error, the build continues anyway and is successful and functional at the end. If I do a first build, make some changes, and then a second one, no error message appears, since the conversionServiceadapter already created by the first build.
So the whole thing works, maybe it is even expected to react like that, but I'd still like not to see an error anywhere in my builds :-).

Errormessage:

[INFO] --- apt-maven-plugin:1.1.3:process (default) @ test ---
/tmp/test/test/src/main/java/org/test/test/config/MapStructConfig.java:5: error: package org.test.test.dto.spring.adapter does not exist
import org.test.test.dto.spring.adapter.ConversionServiceAdapter;
                                           ^
/tmp/test/test/src/main/java/org/test/test/config/MapStructConfig.java:8: error: cannot find symbol
@MapperConfig(componentModel = "spring", uses = ConversionServiceAdapter.class)
                                                ^
  symbol: class ConversionServiceAdapter

pom.xml

	<properties>
		<java.version>11</java.version>
		<org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
		<org.mapstruct.extensions.spring.version>0.1.2</org.mapstruct.extensions.spring.version>
		<m2e.apt.activation>jdt_apt</m2e.apt.activation>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.mapstruct</groupId>
			<artifactId>mapstruct</artifactId>
			<version>${org.mapstruct.version}</version>
		</dependency>
		<dependency>
			<groupId>org.mapstruct.extensions.spring</groupId>
			<artifactId>mapstruct-spring-annotations</artifactId>
			<version>${org.mapstruct.extensions.spring.version}</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>${java.version}</source> <!-- depending on your project -->
					<target>${java.version}</target> <!-- depending on your project -->
					<annotationProcessorPaths>
						<path>
							<groupId>org.mapstruct</groupId>
							<artifactId>mapstruct-processor</artifactId>
							<version>${org.mapstruct.version}</version>
						</path>
						<path>
							<groupId>org.mapstruct.extensions.spring</groupId>
							<artifactId>mapstruct-spring-extensions</artifactId>
							<version>${org.mapstruct.extensions.spring.version}</version>
						</path>
						<path>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok-mapstruct-binding</artifactId>
							<version>0.2.0</version>
						</path>
						<path>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
							<version>${lombok.version}</version>
						</path>
					</annotationProcessorPaths>
				</configuration>
			</plugin>

mapstructconfig

@MapperConfig(componentModel = "spring", uses = ConversionServiceAdapter.class)
@SpringMapperConfig(conversionServiceAdapterPackage ="org.test.test.dto.spring.adapter")
public interface MapStructConfig {
}

Spring Extension ConverterMapperProcessor is not incremental

My project is using the Spring Extension 0.1.0 and my project is constantly rebuilding.

In the gradle log :

Full recompilation is required because org.mapstruct.extensions.spring.converter.ConverterMapperProcessor is not incremental. Analysis took 0.304 secs.

Is it possible to have ConverterMapperProcessor incremental ?

Implicit cyclical dependency

Hi, I started using your development in my application. Faced the following problem.

If the generated mapper uses 'ConversionServiceAdapter' as a dependency then spring not add the mapper to the ConversionService. There is an implicit circular dependency between mapper and 'ConversionServiceAdapter' due to ConversionService. It prevents all converters from being added to the ConversionService.

For example, the generated class might be like this:

`@Component
public class TestConverter implements Converter<S, T> {

private ConversionServiceAdapter serviceAdapter;

public TestConverter(@Lazy ConversionServiceAdapter serviceAdapter) {
    this.serviceAdapter = serviceAdapter;
}

@Override
public T convert(S source) {
    return T;
}

}`

All mappers are created without race for ConversionServiceAdapter, all mappers are added to ConversionService. Otherwise, the mapper that is using the ConversionServiceAdapter will not be added as a converter.
ConverterNotFoundException will be thrown.

Applicable for version 0.0.2.

P.S. If I can help, I can do a PR. I would like to get a fix faster

'No converter found' when injection ConversionService into Spring Service. / indirect reference to ConversionService

Already looked through existing issues but couldn't find anything. (Although this helped me with some other common issues like componentmodel=spring, central config, Injecting the Interface instead of the Adapter, ...)
I created an MVP to show an issue I had in one of my applications.

Given a Mapper like this:

@Mapper(config = MapperSpringConfig.class)
public abstract class ExampleMapper implements Converter<ExampleDTO, Example> {
    /*
        this breaks
     */
    @Autowired
    ExampleService exampleService;

    /*
        this works
     */
//    @Autowired
//    ExampleServiceWithoutConversionService exampleServiceWithoutConversionService;

    @Override
    public abstract Example convert(@Nullable  ExampleDTO exampleDTO);
}

With Services:

// ExampleService.java
@Service
public class ExampleService {
    @Autowired
    ConversionService conversionService;

    // methods that do something ...
}
// ExampleServiceWithoutConversionService.java
@Service
public class ExampleServiceWithoutConversionService {
    // methods that do something ...
}

Central Config:

// MapperSpringConfig.java
@MapperConfig(componentModel = "spring", uses = MyAdapter.class)
@SpringMapperConfig(
        conversionServiceAdapterPackage ="com.example.springmapstructissue",
        conversionServiceAdapterClassName ="MyAdapter"
)
public interface MapperSpringConfig {
}

When running the application like this it results in the following error:

Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [com.example.springmapstructissue.ExampleDTO] to type [com.example.springmapstructissue.Example]
	at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322) ~[spring-core-6.0.11.jar:6.0.11]
	at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195) ~[spring-core-6.0.11.jar:6.0.11]
	at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175) ~[spring-core-6.0.11.jar:6.0.11]
	at com.example.springmapstructissue.SpringMapstructIssueApplication.lambda$commandLineRunner$0(SpringMapstructIssueApplication.java:23) ~[classes/:na]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:771) ~[spring-boot-3.1.3.jar:3.1.3]
	... 5 common frames omitted

But when changing to the other Mapper (ExampleServiceWithoutConversionService.java in this example) then everything works. So something gets broken when injecting a Service into a Mapper that uses ConversionService.

(PS: In the real application this Service also injects a lot of other things like jpa repositories and does more, but not relevant for this problem as this does not result in the issue mentioned - so I also don't think its a Spring Context problem because injecting Repositories and other Services works but something seems special about ConversionService)

Full source of this example available on my Github: https://github.com/SimonFischer04/spring-mapstruct-issue

mapstruct-spring-plus

I created a project named mapstruct-spring-plus to simplify the use of mapstruct. In this project, @AutoMap and @AutoMapField annotations are used to automatically generate Mapper interface files. If I want to merge the code into mapstruct.org,what should I do? Project address: https://github.com/ZhaoRd/mapstruct-spring-plus.
Use code

@Data
@AutoMap(targetType = Car.class)
public class CarDto {
    private String make;

    @AutoMapField(target = "carType")
    private String type;

}

@Data
public class Car {
    private String make;
    private String carType;
}


@ExtendWith(SpringExtension.class)
@ContextConfiguration(
        classes = {AutoMapTests.AutoMapTestConfiguration.class})
public class AutoMapTests {

    @Autowired
    private IObjectMapper mapper;

    @Test
    public void testDtoToEntity() {

        var dto = new CarDto();
        dto.setMake("M1");
        dto.setType("OTHER");

        Car entity = mapper.map(dto, Car.class);

        assertThat(entity).isNotNull();
        assertThat(entity.getMake()).isEqualTo("M1");
        assertThat(entity.getCarType()).isEqualTo("OTHER");

    }


    @ComponentScan("io.github.zhaord.mapstruct.plus")
    @Configuration
    @Component
    static class AutoMapTestConfiguration {


    }


}

How to inject Spring CustomComponent in Mapper interface?

I am trying to achieve the following but not sure if this is what this extensions is meant for. I am a bit confused.

I will like to inject a custom service in the CarMapper interface extending the Spring Converter.

It seems injection is not possible with CarMapper interface but only with abstract classes.

Is it possible to achieve this while keeping the integration with the Spring ConversionService?

@Mapper(componentModel = "spring")
public interface CarMapper extends Converter<Car, CarDto> {

    @Autowired
    private MyCustomService myCustomService;//This is not working

    @Mapping(target = "title", expression = "java(getLocaleTitle(car)))
    CarDto convert(Car car);
    

    default String getLocaleTitle(Car car) {
        return myCustomService.translate(car.getTitle());
    }
}

Usage:

@Autowired
    private ConversionService conversionService;
...
    Car car = ...;
    CarDto carDto = conversionService.convert(car, CarDto.class);

Provide a ConversionService bean if missing

Problem:
Setting up a valid Spring context is quite difficult in tests. Most often the ConversionService provided via WebMvcAutoConfiguration is being used. When it is not present, for example when testing service & persistence layer interaction, can get quite ugly to get all plumbing done. Something we've had to do:

@SpringBootTest({WebMvcAutoConfiguration.class, Test.Config.class})
class Test {

    @Configuration
    @ComponentScan(basePackages = {"com.example.mappers", "com.example.conversionserviceadapterpackage", "com.example.etc"})
    static class Config {
    }

Without this, instantiating Spring context fails in ConversionServiceAdapter due to missing ConversionService bean.
Duplicating this to several test cases gets old quite quick. It is hard to make it portable/re-usable due to how different test context configuration options work: for example, setup with @DataJpaTest looks quite different.

Suggestion:
If detecting that ConversionService bean is missing AND ConversionServiceAdapter base package gets component scanned, should provide a fallback bean with available Converters (at least the ones detected as MapStruct Mappers).

Can be opt-in via a property in SpringMapperConfig or a separate annotation to be placed on test classes.

OR
Provide an example reference documentation on how to conveniently set up a valid Spring context for the ConversionServiceAdapter when Spring web/reactive-web is not present.

Documentation

I am new to MapStruct and am interested in the idea presented by the Spring extension. The doc is non-existent though. It is basically a link to GitHub (with no doc), a Stack Overflow question, and some examples. The noob users are therefore left to synthesize this information into something useful - which is problematic and probably driving users away.

Would be nice to have at least a getting started guide in your readme that explains:

  1. Dependency information for Maven and Gradle.
    • Do I need MapStruct core dependencies?
    • Do I need MapStruct Spring annotations AND extensions or just one or the other?
    • Is Maven central kept up-to-date or do I have to download it directly from GitHub?
  2. Any necessary spring configuration.
  3. One of your examples.

Thanks!

Combining various default starters can result in multiple `ConversionService`s being in the `ApplicationContext`.

Thanks. So combining various default starters can actually result in multiple ConversionServices being in the ApplicationContext. I guess at the very least that would call for an option to specify a bean name at the injection point inside the adapter class. Of course, it still wouldn't solve the auto-registration of the individual Converters. In any case, this has brought up something to work on for the extension project.

Originally posted by @Chessray in mapstruct/mapstruct#3336 (reply in thread)

Fix Code Coverage link

codecov.io still seems to show the results for the default Gradle project. There's no mention of the classes currently in the project.

how can i use "Inverse mappings"

My code like this:
public interface CarMapper extends Converter<Car, CarDTO> { @Override @Mappings( @Mapping(source = "wheels",target = "carWheels") ) CarDTO convert(Car source); }

how can i get a Inversed method "Car convert(CarDTO source);"

External conversions mapper methods are not generated in adapter class

Environment

  • Mapstruct: 1.5.2.Final
  • Mapstruct Sprint Extension: 0.1.2
  • Compiler: Eclipse JDT (IDE) 1.4.100.v20220318-0906
  • Java Language Server - Java: 17.0.4 (Microsoft)
  • Source/Target - Java: 11.0.15 (Temurin)
  • IDE: VSCode

Configuration Code

@MapperConfig(componentModel = "spring")
@SpringMapperConfig(
    externalConversions = @ExternalConversion(sourceType = String.class, targetType = Locale.class))
public interface MapstructConfig {}

Log Output

!ENTRY org.eclipse.jdt.apt.pluggable.core 4 1 2022-08-30 02:26:34.062
!MESSAGE Exception thrown by Java annotation processor org.mapstruct.extensions.spring.converter.ConverterMapperProcessor@2c971632
!STACK 0
java.lang.Exception: java.lang.ClassCastException: class org.eclipse.jdt.internal.compiler.apt.model.AnnotationValueImpl cannot be cast to class javax.lang.model.element.AnnotationMirror (org.eclipse.jdt.internal.compiler.apt.model.AnnotationValueImpl is in unnamed module of loader org.eclipse.osgi.internal.loader.EquinoxClassLoader @61817efb; javax.lang.model.element.AnnotationMirror is in module java.compiler of loader 'platform')
	at org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher.handleProcessor(RoundDispatcher.java:172)
	at org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher.round(RoundDispatcher.java:124)
	at org.eclipse.jdt.internal.compiler.apt.dispatch.BaseAnnotationProcessorManager.processAnnotations(BaseAnnotationProcessorManager.java:172)
	at org.eclipse.jdt.internal.apt.pluggable.core.dispatch.IdeAnnotationProcessorManager.processAnnotations(IdeAnnotationProcessorManager.java:138)
	at org.eclipse.jdt.internal.compiler.Compiler.processAnnotations(Compiler.java:953)
	at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:450)
	at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:426)
	at org.eclipse.jdt.internal.core.builder.AbstractImageBuilder.compile(AbstractImageBuilder.java:379)
	at org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder.compile(IncrementalImageBuilder.java:371)
	at org.eclipse.jdt.internal.core.builder.AbstractImageBuilder.compile(AbstractImageBuilder.java:311)
	at org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder.incrementalBuildLoop(IncrementalImageBuilder.java:190)
	at org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder.build(IncrementalImageBuilder.java:147)
	at org.eclipse.jdt.internal.core.builder.JavaBuilder.buildDeltas(JavaBuilder.java:290)
	at org.eclipse.jdt.internal.core.builder.JavaBuilder.build(JavaBuilder.java:213)
	at org.eclipse.core.internal.events.BuildManager$2.run(BuildManager.java:1024)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:45)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:254)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:311)
	at org.eclipse.core.internal.events.BuildManager$1.run(BuildManager.java:400)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:45)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:403)
	at org.eclipse.core.internal.events.BuildManager.basicBuildLoop(BuildManager.java:514)
	at org.eclipse.core.internal.events.BuildManager.basicBuildLoop(BuildManager.java:462)
	at org.eclipse.core.internal.events.BuildManager.build(BuildManager.java:544)
	at org.eclipse.core.internal.events.AutoBuildJob.doBuild(AutoBuildJob.java:161)
	at org.eclipse.core.internal.events.AutoBuildJob.run(AutoBuildJob.java:255)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:63)
Caused by: java.lang.ClassCastException: class org.eclipse.jdt.internal.compiler.apt.model.AnnotationValueImpl cannot be cast to class javax.lang.model.element.AnnotationMirror (org.eclipse.jdt.internal.compiler.apt.model.AnnotationValueImpl is in unnamed module of loader org.eclipse.osgi.internal.loader.EquinoxClassLoader @61817efb; javax.lang.model.element.AnnotationMirror is in module java.compiler of loader 'platform')
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
	at org.mapstruct.extensions.spring.converter.ConverterMapperProcessor.toSourceTargetTypeNamePairs(ConverterMapperProcessor.java:104)
	at java.base/java.util.Optional.map(Optional.java:260)
	at org.mapstruct.extensions.spring.converter.ConverterMapperProcessor.getExternalConversionMappings(ConverterMapperProcessor.java:95)
	at org.mapstruct.extensions.spring.converter.ConverterMapperProcessor.buildDescriptor(ConverterMapperProcessor.java:77)
	at org.mapstruct.extensions.spring.converter.ConverterMapperProcessor.process(ConverterMapperProcessor.java:63)
	at org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher.handleProcessor(RoundDispatcher.java:142)
	... 26 more

Cyclical dependency keeps happening

Hi guys, i'm trying to create a composed mapper that throws almost the same problem than the #21. Heres is my mappers:

@Mapper(config = MapStructConfiguration.class)
public interface PaymentTypeToPaymentTypeRecord extends Converter<PaymentType, PaymentTypeRecord> {

    @Override
    PaymentTypeRecord convert(final PaymentType paymentType);

}

and the second:

@Mapper(config = MapStructConfiguration.class)
public interface PaymentSubtypeToPaymentSubtypeRecord extends Converter<PaymentSubtype, PaymentSubtypeRecord> {

    @Override
    @Mapping(source = "paymentType", target = "paymentType")
    PaymentSubtypeRecord convert(final PaymentSubtype paymentSubtype);

}

and the generated code:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2021-07-29T00:13:30-0300",
    comments = "version: 1.4.2.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-7.0.2.jar, environment: Java 16.0.1 (Oracle Corporation)"
)
@Component
public class PaymentSubtypeToPaymentSubtypeRecordImpl implements PaymentSubtypeToPaymentSubtypeRecord {

    @Autowired
    private ConversionServiceAdapter conversionServiceAdapter;

    @Override
    public PaymentSubtypeRecord convert(PaymentSubtype paymentSubtype) {
        if ( paymentSubtype == null ) {
            return null;
        }

        PaymentTypeRecord paymentType = null;
        Integer id = null;
        String name = null;
        String icon = null;

        paymentType = conversionServiceAdapter.mapPaymentTypeToPaymentTypeRecord( paymentSubtype.getPaymentType() );
        id = paymentSubtype.getId();
        name = paymentSubtype.getName();
        icon = paymentSubtype.getIcon();

        PaymentSubtypeRecord paymentSubtypeRecord = new PaymentSubtypeRecord( id, name, icon, paymentType );

        return paymentSubtypeRecord;
    }
}

and the exception:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-07-29 00:21:37.357 ERROR 1293694 --- [  restartedMain] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field conversionServiceAdapter in mapper.PaymentSubtypeToPaymentSubtypeRecordImpl required a bean of type 'org.mapstruct.extensions.spring.converter.ConversionServiceAdapter' that could not be found.

can you guys help me with that? i appreciate every kind of help. thanks

Custom mapper definition not working with JDK 17

Hi.
I'm trying to define a mapper convert a Blob to byte array, but the compilation of the project fails with the following error:

class com.squareup.javapoet.ArrayTypeName cannot be cast to class com.squareup.javapoet.ClassName (com.squareup.javapoet.ArrayTypeName and com.squareup.javapoet.ClassName are in unnamed module of loader java.net.URLClassLoader @712cfb63)

Just for reference, the implementation of the mapper is the following:

import org.mapstruct.MapperConfig;
import org.mapstruct.extensions.spring.converter.ConversionServiceAdapter;

/**
 * @author Cosimo Damiano Prete
 * @since 03/02/2022
 */
@MapperConfig(componentModel = "spring", uses = ConversionServiceAdapter.class)
interface SpringMapperConfig {}
import org.mapstruct.Mapper;
import org.springframework.core.convert.converter.Converter;

import javax.validation.constraints.NotNull;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.Blob;
import java.sql.SQLException;

/**
 * @author Cosimo Damiano Prete
 * @since 05/02/2022
 */
@Mapper(uses = SpringMapperConfig.class)
interface BlobToByteArrayMapper extends Converter<Blob, byte[]> {
    @Override
    default byte[] convert(@NotNull Blob blob) {
        try (BufferedInputStream bis = new BufferedInputStream(blob.getBinaryStream());
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             BufferedOutputStream bos = new BufferedOutputStream(baos)) {
            byte[] buffer = new byte[4096];
            boolean keepReading;
            do {
                int bytesRead = bis.read(buffer);
                keepReading = bytesRead != -1;
                if(keepReading) {
                    bos.write(buffer, 0, bytesRead);
                }
            } while (keepReading);

            return baos.toByteArray();
        } catch (SQLException | IOException e) {
            throw new RuntimeException(e);
        }
    }
}

Maven compiler setup:

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <java.version>17</java.version>

        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <maven.compiler.release>${java.version}</maven.compiler.release>
        <maven.compiler.parameters>true</maven.compiler.parameters>
    </properties>

                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.1</version>
                    <configuration>
                        <annotationProcessorPaths>
                            <path>
                                <groupId>org.mapstruct</groupId>
                                <artifactId>mapstruct-processor</artifactId>
                                <version>1.4.2.Final</version>
                            </path>
                            <path>
                                <groupId>org.mapstruct.extensions.spring</groupId>
                                <artifactId>mapstruct-spring-extensions</artifactId>
                                <version>0.1.0</version>
                            </path>
                        </annotationProcessorPaths>
                    </configuration>
                </plugin>

No converter found capable of converting from type X to type Y

I've seen the previous issues in the subject, but event though I inject ConversionService to my code and use the convert() function, I get the error. I've couldn't manage to understand the solution suggested in this comment, but anyway this is not ideal.

Any idea why this can happen? I'm really getting stuck because of this.

I also have another converter on the classpath, if that matters.

Can it be reversed?

Because org.springframework.core.convert.converter.Converter only provides conversion from S to T, if you need to reverse conversion, is there any good way?

ConversionServiceAdapterGenerator does not respect mapstruct.suppressGeneratorTimestamp

The ConversionServiceAdapter generated by ConversionServiceAdapterGenerator contains a "date" value in its @Generated annotation, despite mapstruct.suppressGeneratorTimestamp=true being configured:

@Generated(
    value = "org.mapstruct.extensions.spring.converter.ConversionServiceAdapterGenerator",
    date = "2023-07-05T15:30:39.534896439Z"
)
@Component
public class ConversionServiceAdapter {
    // ...
}

It would be nice if mapstruct-spring-extensions would support this setting (or add a simular one) just like mapstruct does and render without the date attribute:

@Generated(
    value = "org.mapstruct.extensions.spring.converter.ConversionServiceAdapterGenerator"
)
@Component
public class ConversionServiceAdapter {
    // ...
}

I'm mainly thinking "reproducible builds"/"optimizing caching" here, so it would be nice that if my code didn't change, the generated output would stay the same. 🙂

Generate Bridge class for use with ConversionService

We've got a Processor that recognises all Mappers extending Spring's Converter interface. What we'd like to do with this information is to generate a class that contains one method per Mapper which delegates to an injected ConversionService.

value in @Generated annotation incorrect when using conversionServiceAdapterClassName

Hi,

Minor issue...

When I define my Conversion Service to be a different name the @generated annotation has the default value.

@SpringMapperConfig(conversionServiceAdapterClassName ="AvaloqApiMapper")
public @interface AvaloqApiMapperConfig { }

and the generated is

@Generated(
    value = "org.mapstruct.extensions.spring.converter.ConversionServiceAdapterGenerator",
    date = "2021-02-24T11:03:43.603282300Z"
)
@Component
public class AvaloqApiMapper {
...
}

cheers,
Andrew

Use Constructor injection in generated adapter class

Instead of

@Autowired
private ConversionService conversionService;

we prefer

private final ConversionService conversionService;

public ConversionServiceAdapter(final ConversionService conversionService) {
    this.conversionService = conversionService;
}

No converter found capable of converting from type

I have introed mapstruct-spring-extensions and config as the docs told.
mapstruct version is 1.4.2.Final

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct.extensions.spring</groupId>
                            <artifactId>mapstruct-spring-extensions</artifactId>
                            <version>${org.mapstruct.extensions.spring.version}</version>
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>

and SpringMapperConfig

@MapperConfig(componentModel = "spring", uses = ConversionServiceAdapter.class)
@SpringMapperConfig(conversionServiceAdapterPackage = "com.example.converter")
public interface MapStructSpringConfig {
}

converter interface (for some reason, changed type name and field name)

@Mapper(uses = ConversionServiceAdapter.class)
    interface CarStructMapper extends Converter<Car, CarDTO> {
        @Mappings({
                @Mapping(target = "seatCount", source = "seat.total"),
                @Mapping(target = "product", source = "model"),
        })
        CarDTO convert(Car source);
    }

used converter adapter in my class as bellow:


    @Autowired
    private ConversionServiceAdapter conversionServiceAdapter;

    // source is an instance of Car
    conversionServiceAdapter.mapCarToCarDTO(source)

but I got an exception

No converter found capable of converting from type [com.example.Car] to type [com.example.CarDTO]

What should I do to fix it. Thanks

Generate additional delegating mappers for @InheritInverseConfiguration

Hi everyone,

I'd like to define multiple Spring Converter<S, T> implementations (with different generic parameters) in a single @Mapper annotated interface. However, due to type erasure, it is currently not possible to directly declare the same interface multiple times.

Would it be possible to generate additional implementation classes that delegate back to the additional Mapping methods? Currently, we need to manually define a delegate mapper.

Current approach:

@Mapper
public interface MyMapper extends Converter<BarTo, FooTo> {

    @Override
    @Mapping(source = "attribA", target = "attribB")
    FooTo convert(BarTo source);


    @InheritInverseConfiguration
    BarTo convertInverse(FooTo source);


     // Manual delegating mapper required
    @Mapper
    abstract class MyMapperDelegate implements Converter<FooTo, BarTo> {

        @Autowired
        private MyMapper mapper;

        @Override
        public BarTo convert(FooTo source) {
            return this.mapper.convertInverse(source);
        }

    }

    // Generated Mapper Impl
   @Component
   public class MyMapper$MyMapperDelegateImpl extends MyMapperDelegate {}

}

It would be nice if additional Implementation classes could be generated with a simple annotation such as e.g. @GenerateDelegate.

Idea for a future approach:

@Mapper
public interface MyMapper extends Converter<BarTo, FooTo> {

    @Override
    @Mapping(source = "attribA", target = "attribB")
    FooTo convert(BarTo source);

    // Any SAM interface with matching structure
    @GenerateDelegate(class = org.springframework.core.convert.converter.Converter.class)
    @InheritInverseConfiguration
    BarTo convertInverse(FooTo source);


     // Generated Mapper Impl
    @Component
    public class MyMapperDelegateImpl implements Converter<FooTo, BarTo> {
      
        @Autowired
        private MyMapper mapper;

        @Override
        public BarTo convert(FooTo source) {
            return this.mapper.convertInverse(source);
        }
    }
}

I'm asking this here since it would be especially useful together with the Spring Converter Interface, and would potentially also require an entry in the ConversionServiceAdapter.

Map lists

It seems that mapping lists is not really possible, the generated code is not able to understand the type of the list:

public List<MyTarget> mapListToList(final List<MySource> source) {
       return conversionService.convert(source,List.class)
}

I have also the same generated method 2 times if I have the conversion in the both directions. the compiler end up saying "name clash: mapListToList.... and mapListToList have the same type erasure"

For lists, another method should be used in the interface ConversionService (see this stackoverflow):

Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType)

It would be nice if the documentation could say something about this

"No converter found capable of converting from type" in Spring Boot Starter Web project

Related to #22

We're trying to integrate mapstruct-spring-extensions into a project and hit a little bit of a roadblock.

I was under the impression that, when defining a mapper, extending Converter and adding a @Mapper annotation would be enough for ConversionService to pick this mapper up automatically. However, this isn't happening in my Spring Boot project with Spring Boot Starter Web.

This is how I defined a Mapper:

@Mapper
interface FooMapper : Converter<Foo, FooDto> {
    override fun convert(source: Foo): FooDto
}

I've built an MCVE so that you can verify my test setup.

FooMapperImpl is being generated as expected and so is ConversionServiceAdapter, and yet, ConversionService fails to convert from Foo to FooDto.

Am I missing something here? Is there some configuration I need to do after all? I feel like I exhausted the available documentation on this issue. Thanks in advance!

edit: It doesn't seem to be a problem with Spring. I've just tested it, and Converters that aren't generated are detected automatically.

Add user documentation

  • How to set up a project in Maven and Gradle
  • Add links to Spring and explanation on the role of the bridge class.

Adapter method will not be generated for below converter even though `mapstruct` able generate the implementation class.

    Adapter method will not be generated for below converter even though `mapstruct` able generate the implementation class.
@Mapper(config = MapperConfiguration.class)
interface MyConverter extends Converter<Foo, FooDto> {
}

To be able to generate the method for adapter class, have to declare convert method again as below.

@Mapper(config = MapperConfiguration.class)
interface MyConverter extends Converter<Foo, FooDto> {
   FooDto convert(Foo source);
}

Originally posted by @myatmin in #53 (comment)

Utilize standard Spring conversions

Spring provides a rich set of standard converters that go beyond MapStruct's built-in functionalities (e.g. UUID to String). It would be beneficial if we could utilize these in order to reduce the burden on the MapStruct user who has to reimplement these in every project.

Error when compiling with Eclipse

    With following environment  `compiler: Eclipse JDT (IDE) 1.4.200.v20220802-0458, environment: Java 17.0.4.1 (Eclipse Adoptium)`, adapter class will NOT be generating method for all of the converters, but it only generate the class.
[Error - 4:53:17 AM] Sep 30, 2022, 4:53:17 AM Exception thrown by Java annotation processor org.mapstruct.extensions.spring.converter.ConverterMapperProcessor@67b39ba
java.lang.ClassCastException: class org.eclipse.jdt.internal.compiler.apt.model.AnnotationValueImpl cannot be cast to class javax.lang.model.element.AnnotationMirror (org.eclipse.jdt.internal.compiler.apt.model.AnnotationValueImpl is in unnamed module of loader org.eclipse.osgi.internal.loader.EquinoxClassLoader @6f270916; javax.lang.model.element.AnnotationMirror is in module java.compiler of loader 'platform')
java.lang.Exception: java.lang.ClassCastException: class org.eclipse.jdt.internal.compiler.apt.model.AnnotationValueImpl cannot be cast to class javax.lang.model.element.AnnotationMirror (org.eclipse.jdt.internal.compiler.apt.model.AnnotationValueImpl is in unnamed module of loader org.eclipse.osgi.internal.loader.EquinoxClassLoader @6f270916; javax.lang.model.element.AnnotationMirror is in module java.compiler of loader 'platform')
	at org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher.handleProcessor(RoundDispatcher.java:172)
	at org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher.round(RoundDispatcher.java:124)
	at org.eclipse.jdt.internal.compiler.apt.dispatch.BaseAnnotationProcessorManager.processAnnotations(BaseAnnotationProcessorManager.java:172)
	at org.eclipse.jdt.internal.apt.pluggable.core.dispatch.IdeAnnotationProcessorManager.processAnnotations(IdeAnnotationProcessorManager.java:138)
	at org.eclipse.jdt.internal.compiler.Compiler.processAnnotations(Compiler.java:953)
	at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:450)
	at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:426)
	at org.eclipse.jdt.internal.core.builder.AbstractImageBuilder.compile(AbstractImageBuilder.java:379)
	at org.eclipse.jdt.internal.core.builder.BatchImageBuilder.compile(BatchImageBuilder.java:214)
	at org.eclipse.jdt.internal.core.builder.AbstractImageBuilder.compile(AbstractImageBuilder.java:311)
	at org.eclipse.jdt.internal.core.builder.BatchImageBuilder.build(BatchImageBuilder.java:79)
	at org.eclipse.jdt.internal.core.builder.JavaBuilder.buildAll(JavaBuilder.java:273)
	at org.eclipse.jdt.internal.core.builder.JavaBuilder.build(JavaBuilder.java:188)
	at org.eclipse.core.internal.events.BuildManager$2.run(BuildManager.java:1024)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:45)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:254)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:311)
	at org.eclipse.core.internal.events.BuildManager$1.run(BuildManager.java:400)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:45)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:403)
	at org.eclipse.core.internal.events.BuildManager.basicBuildLoop(BuildManager.java:514)
	at org.eclipse.core.internal.events.BuildManager.basicBuildLoop(BuildManager.java:462)
	at org.eclipse.core.internal.events.BuildManager.build(BuildManager.java:544)
	at org.eclipse.core.internal.resources.Workspace.buildInternal(Workspace.java:524)
	at org.eclipse.core.internal.resources.Workspace.build(Workspace.java:420)
	at org.eclipse.jdt.ls.core.internal.handlers.BuildWorkspaceHandler.buildProjects(BuildWorkspaceHandler.java:112)
	at org.eclipse.jdt.ls.core.internal.handlers.JDTLanguageServer.lambda$28(JDTLanguageServer.java:885)
	at org.eclipse.jdt.ls.core.internal.handlers.JDTLanguageServer.lambda$55(JDTLanguageServer.java:1070)
	at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(Unknown Source)
	at java.base/java.util.concurrent.CompletableFuture$Completion.exec(Unknown Source)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(Unknown Source)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(Unknown Source)
	at java.base/java.util.concurrent.ForkJoinPool.scan(Unknown Source)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(Unknown Source)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(Unknown Source)
Caused by: java.lang.ClassCastException: class org.eclipse.jdt.internal.compiler.apt.model.AnnotationValueImpl cannot be cast to class javax.lang.model.element.AnnotationMirror (org.eclipse.jdt.internal.compiler.apt.model.AnnotationValueImpl is in unnamed module of loader org.eclipse.osgi.internal.loader.EquinoxClassLoader @6f270916; javax.lang.model.element.AnnotationMirror is in module java.compiler of loader 'platform')
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(Unknown Source)
	at java.base/java.util.stream.AbstractPipeline.copyInto(Unknown Source)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(Unknown Source)
	at java.base/java.util.stream.AbstractPipeline.evaluate(Unknown Source)
	at java.base/java.util.stream.ReferencePipeline.collect(Unknown Source)
	at org.mapstruct.extensions.spring.converter.ConverterMapperProcessor.toSourceTargetTypeNamePairs(ConverterMapperProcessor.java:104)
	at java.base/java.util.Optional.map(Unknown Source)
	at org.mapstruct.extensions.spring.converter.ConverterMapperProcessor.getExternalConversionMappings(ConverterMapperProcessor.java:95)
	at org.mapstruct.extensions.spring.converter.ConverterMapperProcessor.buildDescriptor(ConverterMapperProcessor.java:77)
	at org.mapstruct.extensions.spring.converter.ConverterMapperProcessor.process(ConverterMapperProcessor.java:63)
	at org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher.handleProcessor(RoundDispatcher.java:142)
	... 34 more

Originally posted by @myatmin in #53 (comment)

Name collision with same class name in different packages

          @Chessray I'm experiencing a similar name collision. When defining mappers for classes with the same name but different packages, the generated name in the ConversionServiceAdapter is the same:
@Mapper(config = SharedConfig.class)
public interface MyMapper extends Converter<Foo, com.contoso.Bar> {
    com.contoso.Bar convert(Foo source);
}

@Mapper(config = SharedConfig.class)
public interface MyMapper extends Converter<Foo, com.fabrikam.Bar> {
    com.fabrikam.Bar convert(Foo source);
}

// With classes
class Foo {
}

package com.contoso;
class Bar {
}

package com.fabrikam;
class Bar {
}

Is there a simple way around this, or does that require changes to the name generator as well?

Originally posted by @pw-lehre in #86 (comment)

Compiler Error with Qualified Mapping and Custom Converter - Duplicate Method Generation

Let say I create two classes Source:

public class Source {
    public byte fieldA;
    public byte fieldB;
    public byte fieldC;
    public byte fieldD;
}

and Target:

public class Target {
    public byte fieldE;
    public byte fieldF;
}

Then I create a new mapper for the two classes:

@Mapper(config = MapperTestConfig.class)
public interface SourceToTargetConverter extends Converter<Source, Target> {

  @Mapping(target = "fieldE", source = "source")
  @Mapping(
      target = "fieldF",
      source = "source",
      qualifiedByName = {"cAndD", "toF"})
  @Override
  Target convert(Source source);
}

And then I create mappers for each of the two operations:

@Mapper
public interface FieldsAPlusBToFieldEMapper extends Converter<Source, Byte> {

  @Override
  default Byte convert(Source source) {
      return (byte) (source.fieldA + source.fieldB);
  }
}

and:

@Named("cAndD")
@Mapper
public interface FieldsCPlusDToFieldFMapper extends Converter<Source, Byte> {

  @Named("toF")
  @Override
  default Byte convert(Source source) {
      return (byte) (source.fieldC + source.fieldD);
  }
}

Finally, I create the @MapperConfig:

@MapperConfig(componentModel = MappingConstants.ComponentModel.SPRING)
@SpringMapperConfig
public interface MapperTestConfig {}


Result: Two methods with the same are passed down to ConversionServiceAdapter, thus creating a compiler error:

@Component
public class ConversionServiceAdapter {
  private final ConversionService conversionService;

  public ConversionServiceAdapter(@Lazy final ConversionService conversionService) {
    this.conversionService = conversionService;
  }

  public Byte mapSourceToByte(final Source source) {
    return (Byte) conversionService.convert(source, TypeDescriptor.valueOf(Source.class), TypeDescriptor.valueOf(Byte.class));
  }

  public Byte mapSourceToByte(final Source source) {
    return (Byte) conversionService.convert(source, TypeDescriptor.valueOf(Source.class), TypeDescriptor.valueOf(Byte.class));
  }

  public Target mapSourceToTarget(final Source source) {
    return (Target) conversionService.convert(source, TypeDescriptor.valueOf(Source.class), TypeDescriptor.valueOf(Target.class));
  }
}

Stacktrace (redacted):

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.10.1:compile (default-compile) on project [name]: Compilation failure: Compilation failure:
[ERROR] [generated-annotation-path]/ConversionServiceAdapter.java:[30,15] method mapSourceToByte(Source) is already defined in class ConversionServiceAdapter
[ERROR] [source-file-path]/FieldsCPlusDToFieldFMapper.java:[9,8] No implementation was created for FieldsCPlusDToFieldFMapper due to having a problem in the erroneous element java.util.ArrayList. Hint: this often means that some other annotation processor was supposed to process the erroneous element. You can also enable MapStruct verbose mode by setting -Amapstruct.verbose=true as a compilation argument.
[ERROR] [source-file-path]/SourceToTargetConverter.java:[8,8] No implementation was created for SourceToTargetConverter due to having a problem in the erroneous element java.util.ArrayList. Hint: this often means that some other annotation processor was supposed to process the erroneous element. You can also enable MapStruct verbose mode by setting -Amapstruct.verbose=true as a compilation argument.
[ERROR] [source-file-path]/FieldsAPlusBToFieldEMapper.java:[7,8] No implementation was created for FieldsAPlusBToFieldEMapper due to having a problem in the erroneous element java.util.ArrayList. Hint: this often means that some other annotation processor was supposed to process the erroneous element. You can also enable MapStruct verbose mode by setting -Amapstruct.verbose=true as a compilation argument.

Expected:
The enqine should generate two methods with different signatures, each resolving the qualifications specified with (at)Named.

@Component
public class ConversionServiceAdapter {
  // fields and constructor. Might use more than one ConversionService to qualify different methods.
  public Byte mapSourceToByte(final Source source) {
    return (Byte) conversionService.convert(source, TypeDescriptor.valueOf(Source.class), TypeDescriptor.valueOf(Byte.class));
  }
 
  // Method resolved with (at)Named = "cAndD" and "toF".
  public Byte mapSourceToByteCandDtoF(final Source source) {
    // logic for qualified mapping, maybe using another ConversionService.
  }

  public Target mapSourceToTarget(final Source source) {
    return (Target) conversionService.convert(source, TypeDescriptor.valueOf(Source.class), TypeDescriptor.valueOf(Target.class));
  }
}

and then use them in the converter:

@Component
public class SourceToTargetConverterImpl implements SourceToTargetConverter {

    @Autowired
    private ConversionServiceAdapter conversionServiceAdapter;

    @Override
    public Target convert(Source source) {
        if ( source == null ) {
            return null;
        }

        Target target = new Target();

        target.fieldE = conversionServiceAdapter.mapSourceToByte( source );
        target.fieldF = conversionServiceAdapter.mapSourceToBytecAndDtoF( source );

        return target;
    }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.