mapstruct/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc

200 lines
6.2 KiB
Plaintext

[[using-spi]]
== Using the MapStruct SPI
=== Custom Accessor Naming Strategy
MapStruct offers the possibility to override the `AccessorNamingStrategy` via the Service Provide Interface (SPI). A nice example is the use of the fluent API on the source object `GolfPlayer` and `GolfPlayerDto` below.
.Source object GolfPlayer with fluent API.
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
public class GolfPlayer {
private double handicap;
private String name;
public double handicap() {
return handicap;
}
public GolfPlayer withHandicap(double handicap) {
this.handicap = handicap;
return this;
}
public String name() {
return name;
}
public GolfPlayer withName(String name) {
this.name = name;
return this;
}
}
----
====
.Source object GolfPlayerDto with fluent API.
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
public class GolfPlayerDto {
private double handicap;
private String name;
public double handicap() {
return handicap;
}
public GolfPlayerDto withHandicap(double handicap) {
this.handicap = handicap;
return this;
}
public String name() {
return name;
}
public GolfPlayerDto withName(String name) {
this.name = name;
return this
}
}
----
====
We want `GolfPlayer` to be mapped to a target object `GolfPlayerDto` similar like we 'always' do this:
.Source object with fluent API.
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
public interface GolfPlayerMapper {
GolfPlayerMapper INSTANCE = Mappers.getMapper( GolfPlayerMapper.class );
GolfPlayerDto toDto(GolfPlayer player);
GolfPlayer toPlayer(GolfPlayerDto player);
}
----
====
This can be achieved with implementing the SPI `org.mapstruct.ap.spi.AccessorNamingStrategy` as in the following example. Here's an implemented `org.mapstruct.ap.spi.AccessorNamingStrategy`:
.CustomAccessorNamingStrategy
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
/**
* A custom {@link AccessorNamingStrategy} recognizing getters in the form of {@code property()} and setters in the
* form of {@code withProperty(value)}.
*/
public class CustomAccessorNamingStrategy extends DefaultAccessorNamingStrategy {
@Override
public boolean isGetterMethod(ExecutableElement method) {
String methodName = method.getSimpleName().toString();
return !methodName.startsWith( "with" ) && method.getReturnType().getKind() != TypeKind.VOID;
}
@Override
public boolean isSetterMethod(ExecutableElement method) {
String methodName = method.getSimpleName().toString();
return methodName.startsWith( "with" ) && methodName.length() > 4;
}
@Override
public String getPropertyName(ExecutableElement getterOrSetterMethod) {
String methodName = getterOrSetterMethod.getSimpleName().toString();
return IntrospectorUtils.decapitalize( methodName.startsWith( "with" ) ? methodName.substring( 4 ) : methodName );
}
}
----
====
The `CustomAccessorNamingStrategy` makes use of the `DefaultAccessorNamingStrategy` (also available in mapstruct-processor) and relies on that class to leave most of the default behaviour unchanged.
To use a custom SPI implementation, it must be located in a separate JAR file together with the file `META-INF/services/org.mapstruct.ap.spi.AccessorNamingStrategy` with the fully qualified name of your custom implementation as content (e.g. `org.mapstruct.example.CustomAccessorNamingStrategy`). This JAR file needs to be added to the annotation processor classpath (i.e. add it next to the place where you added the mapstruct-processor jar).
[TIP]
Fore more details: The example above is present in our examples repository (https://github.com/mapstruct/mapstruct-examples).
[mapping-exclusion-provider]
=== Mapping Exclusion Provider
MapStruct offers the possibility to override the `MappingExclusionProvider` via the Service Provider Interface (SPI).
A nice example is to not allow MapStruct to create an automatic sub-mapping for a certain type,
i.e. MapStruct will not try to generate an automatic sub-mapping method for an excluded type.
[NOTE]
====
The `DefaultMappingExclusionProvider` will exclude all types under the `java` or `javax` packages.
This means that MapStruct will not try to generate an automatic sub-mapping method between some custom type and some type declared in the Java class library.
====
.Source object
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
include::{processor-ap-test}/nestedbeans/exclusions/custom/Source.java[tag=documentation]
----
====
.Target object
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
include::{processor-ap-test}/nestedbeans/exclusions/custom/Target.java[tag=documentation]
----
====
.Mapper definition
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
include::{processor-ap-test}/nestedbeans/exclusions/custom/ErroneousCustomExclusionMapper.java[tag=documentation]
----
====
We want to exclude the `NestedTarget` from the automatic sub-mapping method generation.
.CustomMappingExclusionProvider
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
include::{processor-ap-test}/nestedbeans/exclusions/custom/CustomMappingExclusionProvider.java[tag=documentation]
----
====
To use a custom SPI implementation, it must be located in a separate JAR file
together with the file `META-INF/services/org.mapstruct.ap.spi.MappingExclusionProvider` with the fully qualified name of your custom implementation as content
(e.g. `org.mapstruct.example.CustomMappingExclusionProvider`).
This JAR file needs to be added to the annotation processor classpath
(i.e. add it next to the place where you added the mapstruct-processor jar).
[[custom-builder-provider]]
=== Custom Builder Provider
MapStruct offers the possibility to override the `DefaultProvider` via the Service Provider Interface (SPI).
A nice example is to provide support for a custom builder strategy.
.Custom Builder Provider which disables Builder support
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
include::{processor-ap-main}/spi/NoOpBuilderProvider.java[tag=documentation]
----
====