mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#1417 Add documentation about the builder support
This commit is contained in:
parent
5834368b15
commit
43a9419c33
@ -5,6 +5,8 @@
|
||||
:Author: Gunnar Morling, Andreas Gudian, Sjaak Derksen, Filip Hrisafov and the MapStruct community
|
||||
:processor-dir: ../../../../processor
|
||||
: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
|
||||
@ -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()`.
|
||||
====
|
||||
|
||||
[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]
|
||||
====
|
||||
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]
|
||||
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 a mapper
|
||||
|
||||
@ -2671,3 +2795,19 @@ together with the file `META-INF/services/org.mapstruct.ap.spi.MappingExclusionP
|
||||
(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 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]
|
||||
----
|
||||
====
|
||||
|
@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.mapstruct.itest.immutables.extras;
|
||||
|
||||
// tag::documentation[]
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
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.TypeHierarchyErroneousException;
|
||||
|
||||
// end::documentation[]
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
// tag::documentation[]
|
||||
public class ImmutablesBuilderProvider extends DefaultBuilderProvider {
|
||||
|
||||
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 );
|
||||
}
|
||||
else {
|
||||
// Immutables processor has not run yet. Trigger a postpone to the next round for MapStruct
|
||||
throw new TypeHierarchyErroneousException( typeElement );
|
||||
}
|
||||
}
|
||||
@ -95,3 +101,4 @@ public class ImmutablesBuilderProvider extends DefaultBuilderProvider {
|
||||
return elements.getTypeElement( builderQualifiedName );
|
||||
}
|
||||
}
|
||||
// end::documentation[]
|
||||
|
Loading…
x
Reference in New Issue
Block a user