This example demonstrates how easy it is to use MapStruct in a Quarkus application.
The example was basically built with the following steps:
-
Create a new Quarkus application
mvn io.quarkus:quarkus-maven-plugin:1.0.1.Final:create \ -DprojectGroupId=org.mapstruct.examples.quarkus \ -DprojectArtifactId=mapstruct-examples-quarkus \ -DclassName="org.mapstruct.example.quarkus.PersonResource" \ -Dpath="/person" \ -Dextensions="resteasy-jsonb" -
Add
mapstructas a dependency<dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version> <scope>provided</scope> </dependency>
-
Add the
mapstruct-processoras a regularprovidedscoped dependency and not as described in the reference guide<dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> <scope>provided</scope> </dependency>
-
Set CDI as the default component-model (see reference guide)
-
Define a mapper and inject it with
@Injectin the service
That's it!
As MapStruct is not using reflection within the mappers native images are supported as long as CDI is used to retrieve the mappers. Just try to package your Quarkus application in a native image and feel the speed!
mvn package -Dnative
In case you prefer to use Mappers.getMapper(...) be aware that this is not automatically supported by native images. Internally the getMapper method uses reflection to
retrieve the mapper implementation.
This is the only place where MapStruct uses reflection and thus it causes issues with native images.
There are some workarounds by defining additional metadata used during native image creation by GraalVM. For example you can create a Feature as described
here and register all you mapper implementations and their constructor(s).
For example if you just have a PersonMapper the feature might look like this:
@AutomaticFeature
class MapstructMapperFeature implements Feature {
public void beforeAnalysis(BeforeAnalysisAccess access) {
try {
Class<?> mapperClass = Class.forName( "org.mapstruct.example.quarkus.mapper.PersonMapperImpl" );
RuntimeReflection.register( mapperClass );
RuntimeReflection.register( mapperClass.getConstructors() );
}
catch ( ClassNotFoundException e ) {
throw new RuntimeException( e );
}
}
}When using the Quarkus dev mode the mapper will automatically be (re)generated in case you make change to the @Mapper annotated class.
Changing a dependent class (like PersonDto in this example) will not (re)generate the mapper implementation.
As a workaround you can quit the dev-mode, recompile and start the application once again or you can make a (temporary) change to the @Mapper annotated class so that it
will be picked up and a valid implementation will be generated.
Pay attention: You have to add the mapstruct-processor as described above and not using the maven-compile-plugin.