#1417 Add documentation about the builder support

This commit is contained in:
Filip Hrisafov 2018-04-15 10:31:45 +02:00 committed by GitHub
parent 5834368b15
commit 43a9419c33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 147 additions and 0 deletions

View File

@ -5,6 +5,8 @@
:Author: Gunnar Morling, Andreas Gudian, Sjaak Derksen, Filip Hrisafov and the MapStruct community :Author: Gunnar Morling, Andreas Gudian, Sjaak Derksen, Filip Hrisafov and the MapStruct community
:processor-dir: ../../../../processor :processor-dir: ../../../../processor
:processor-ap-test: {processor-dir}/src/test/java/org/mapstruct/ap/test :processor-ap-test: {processor-dir}/src/test/java/org/mapstruct/ap/test
:integration-tests-dir: ../../../../integrationtest
:immutables-builder-test: {integration-tests-dir}/src/test/resources/immutablesBuilderTest
[[Preface]] [[Preface]]
== Preface == Preface
@ -310,6 +312,21 @@ In the generated method implementations all readable properties from the source
The property name as defined in the http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html[JavaBeans specification] must be specified in the `@Mapping` annotation, e.g. _seatCount_ for a property with the accessor methods `getSeatCount()` and `setSeatCount()`. The property name as defined in the http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html[JavaBeans specification] must be specified in the `@Mapping` annotation, e.g. _seatCount_ for a property with the accessor methods `getSeatCount()` and `setSeatCount()`.
==== ====
[TIP]
====
Fluent setters are also supported.
Fluent setters are setters that return the same type as the type being modified.
E.g.
```
public Builder seatCount(int seatCount) {
this.seatCount = seatCount;
return this;
}
```
====
[TIP] [TIP]
==== ====
When using Java 8 or later, you can omit the `@Mappings` wrapper annotation and directly specify several `@Mapping` annotations on one method. When using Java 8 or later, you can omit the `@Mappings` wrapper annotation and directly specify several `@Mapping` annotations on one method.
@ -594,6 +611,113 @@ You can find the complete example in the
https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-field-mapping[mapstruct-examples-field-mapping] https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-field-mapping[mapstruct-examples-field-mapping]
project on GitHub. project on GitHub.
[[mapping-with-builders]]
=== Using builders
MapStruct also supports mapping of immutable types via builders.
When performing a mapping MapStruct checks if there is a builder for the type being mapped.
This is done via the `BuilderProvider` SPI.
If a Builder exists for a certain type, than that builder will be used for the mappings.
The default implementation of the `BuilderProvider` assumes the following:
* The type has a parameterless public static builder creation method that returns a builder.
So for example `Person` has a public static method that returns `PersonBuilder`.
* The builder type has a parameterless public method (build method) that returns the type being build
In our example `PersonBuilder` has a method returning `Person`.
If such type is found then MapStruct will use that type to perform the mapping to (i.e. it will look for setters into that type).
To finish the mapping MapStruct generates code that will invoke the build method of the builder.
[NOTE]
======
The <<object-factories>> are also considered for the builder type.
E.g. If an object factory exists for our `PersonBuilder` than this factory would be used instead of the builder creation method.
======
.Person with Builder example
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
public class Person {
private final String name;
protected Person(Person.Builder builder) {
this.name = builder.name;
}
public static Person.Builder builder() {
return new Person.Builder();
}
public static class Builder {
private String name;
public Builder name(String name) {
this.name = name;
return this;
}
public Person create() {
return new Person( this );
}
}
}
----
====
.Person Mapper definition
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
public interface PersonMapper {
Person map(PersonDto dto);
}
----
====
.Generated mapper with builder
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
// GENERATED CODE
public class PersonMapperImpl implements PersonMapper {
public Person map(PersonDto dto) {
if (dto == null) {
return null;
}
Person.Builder builder = Person.builder();
builder.name( dto.getName() );
return builder.create();
}
}
----
====
Supported builder frameworks:
* https://projectlombok.org/[Lombok] - requires having the Lombok classes in a separate module. See for more information https://github.com/rzwitserloot/lombok/issues/1538[rzwitserloot/lombok#1538]
* https://github.com/google/auto/blob/master/value/userguide/index.md[AutoValue]
* https://immutables.github.io/[Immutables] - requires using a custom `AccessorNamingStrategy` and a custom `BuilderProvider` (see example in integration tests)
* https://github.com/google/FreeBuilder[FreeBuilder]
* It also works for custom builders (handwritten ones) if the implementation supports the defined rules for the default `BuilderProvider`.
Otherwise, you would need to write a custom `BuilderProvider`
[TIP]
====
In case you want to disable using builders then you can use the `NoOpBuilderProvider` by creating a `org.mapstruct.ap.spi.BuilderProvider` file in the `META-INF/services` directory with `org.mapstruct.ap.spi.NoOpBuilderProvider` as it's content.
====
[[retrieving-mapper]] [[retrieving-mapper]]
== Retrieving a mapper == Retrieving a mapper
@ -2671,3 +2795,19 @@ together with the file `META-INF/services/org.mapstruct.ap.spi.MappingExclusionP
(e.g. `org.mapstruct.example.CustomMappingExclusionProvider`). (e.g. `org.mapstruct.example.CustomMappingExclusionProvider`).
This JAR file needs to be added to the annotation processor classpath 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). (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 for Immutables
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
include::{immutables-builder-test}/extras/src/main/java/org/mapstruct/itest/immutables/extras/ImmutablesBuilderProvider.java[tag=documentation]
----
====

View File

@ -18,6 +18,8 @@
*/ */
package org.mapstruct.itest.immutables.extras; package org.mapstruct.itest.immutables.extras;
// tag::documentation[]
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
@ -32,9 +34,12 @@ import org.mapstruct.ap.spi.BuilderInfo;
import org.mapstruct.ap.spi.DefaultBuilderProvider; import org.mapstruct.ap.spi.DefaultBuilderProvider;
import org.mapstruct.ap.spi.TypeHierarchyErroneousException; import org.mapstruct.ap.spi.TypeHierarchyErroneousException;
// end::documentation[]
/** /**
* @author Filip Hrisafov * @author Filip Hrisafov
*/ */
// tag::documentation[]
public class ImmutablesBuilderProvider extends DefaultBuilderProvider { public class ImmutablesBuilderProvider extends DefaultBuilderProvider {
private static final Pattern JAVA_JAVAX_PACKAGE = Pattern.compile( "^javax?\\..*" ); private static final Pattern JAVA_JAVAX_PACKAGE = Pattern.compile( "^javax?\\..*" );
@ -70,6 +75,7 @@ public class ImmutablesBuilderProvider extends DefaultBuilderProvider {
return super.findBuilderInfo( immutableElement, elements, types ); return super.findBuilderInfo( immutableElement, elements, types );
} }
else { else {
// Immutables processor has not run yet. Trigger a postpone to the next round for MapStruct
throw new TypeHierarchyErroneousException( typeElement ); throw new TypeHierarchyErroneousException( typeElement );
} }
} }
@ -95,3 +101,4 @@ public class ImmutablesBuilderProvider extends DefaultBuilderProvider {
return elements.getTypeElement( builderQualifiedName ); return elements.getTypeElement( builderQualifiedName );
} }
} }
// end::documentation[]