使用构建器(使用immutables注释处理器)将映射对象映射到不可变对象映射
我们使用immutables framework来生成所有DTO。现在我们想用mapstruct将这些对象映射到另一个。但生成的DTO是不可变的,并且没有setter,也没有构造器,对应于构建器模式。它们只能通过由静态builder()
-方法访问的相应构建器填充。使用构建器(使用immutables注释处理器)将映射对象映射到不可变对象映射
我们试着将DTO1映射到DTO2.Builder,它可以工作,如果地图可以识别Builder中的setter,但它们不具有void返回类型,但返回Builder自身以进行流畅并置。
所以这里是这个例子的代码。
我们有两个接口
@Value.Immutable
public interface MammalDto {
public Integer getNumberOfLegs();
public Long getNumberOfStomachs();
}
和
@Value.Immutable
public interface MammalEntity {
public Long getNumberOfLegs();
public Long getNumberOfStomachs();
}
然后我们有mapstruct映射器接口:
@Mapper(uses = ObjectFactory.class)
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper(SourceTargetMapper.class);
ImmutableMammalEntity.Builder toTarget(MammalDto source);
}
对于mapstruct找到我们需要一个工厂的生成器:
public class ObjectFactory {
public ImmutableMammalDto.Builder createMammalDto() {
return ImmutableMammalDto.builder();
}
public ImmutableMammalEntity.Builder createMammalEntity() {
return ImmutableMammalEntity.builder();
}
}
为了生成代码的编译器插件奉命同时使用注释处理器:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
<version>2.2.8</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.2.0.Beta3</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
注意:这种方法只与版本mapstruct> 1.2.x.较旧的版本在干净版本(mvn clean compile
)中存在问题,即他们没有找到刚建立的不可变源。在第二个版本(没有清理)中,他们会发现不可变的实现,因为它们在运行注释处理器之前处于类路径中。这个bug现在已经修复。
这工作就像一个魅力。首先生成接口的不可变实现,地图使用它们生成构建器。
但测试表明,没有属性被设置:
@Test
public void test() {
MammalDto s = ImmutableMammalDto.builder().numberOfLegs(4).numberOfStomachs(3l).build();
MammalEntity t = SourceTargetMapper.MAPPER.toTarget(s).build();
assertThat(t.getNumberOfLegs()).isEqualTo(4);
assertThat(t.getNumberOfStomachs()).isEqualTo(3);
}
的断言失败。在由mapstruct产生映射器一看就表明,它显然没有发现任何setter方法:返回
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
//...
)
public class SourceTargetMapperImpl implements SourceTargetMapper {
private final ObjectFactory objectFactory = new ObjectFactory();
@Override
public Builder toTarget(MammalDto source) {
if (source == null) {
return null;
}
Builder builder = objectFactory.createMammalEntity();
return builder;
}
}
空建设者。我想原因是二传手实施产生的建设者,因为它返回自己创建一个流畅的API:
public final Builder numberOfLegs(Long numberOfLegs) {
this.numberOfLegs = Objects.requireNonNull(numberOfLegs, "numberOfLegs");
return this;
}
有没有办法让mapstruct找到这些制定者?或者甚至有更好的方式来与建设者打交道这样的不可变对象?
编辑:正如我在评论中指出的,我碰到了Issue #782。在版本1.2.0中.Beta3构建器仍然不受支持。但是有几个关于这个话题的讨论,所以如果一个人有同样的问题,可能会很有趣。
我们对我们的项目有同样的问题。 作为解决方法,我们一直在使用Modifiable
实现我们不可变的dto。
你也可以试试。直接使用建设者和对象工厂更好。
@Value.Modifiable
用setter生成实现。
@Value.Style(create = "new")
生成公共无参数构造函数。
@Value.Immutable
@Value.Modifiable
@Value.Style(create = "new")
public interface MammalEntity {
public Long getNumberOfLegs();
public Long getNumberOfStomachs();
}
那么你的映射器会更简单,不需要在对象工厂。
@Mapper
public interface SourceTargetMapper {
ModifiableMammalEntity toTarget(MammalDto source);
}
在这种情况下MapStruct可以看到这样的映射意志ModifiableMammalEntity
用法制定者看起来像
// Here you don't need to worry about implementation of MammalEntity is. The interface `MammalEntity` is immutable.
MammalEntity mammalEntity = sourceTargetMapper.toTarget(source);
好点,谢谢你分享你的想法!如果使用不可变对象作为getter/setter,equals,hashCode和toString(这是一个有效的用例)的简单代码生成器,那么这是一个不错的选择。 (顺便说一下,不可变对象也是一个很好的工具!)但对我们来说,不可变性是我们选择不可变构架的中心价值。我们不想牺牲这种好处。 –
您可以配置Immutables产生的建设者制定者:
@Value.Immutable
@Value.Style(init = "set*")
public interface MammalEntity {
public Long getNumberOfLegs();
public Long getNumberOfStomachs();
}
而且你不需要ObjectBuilder,你可以直接使用gen erated不可变类
@Mapper(uses = ImmutableMammalEntity.class)
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper(SourceTargetMapper.class);
ImmutableMammalEntity.Builder toTarget(MammalDto source);
}
,你甚至可以在自己的注释
@Value.Style(init = "set*")
public @interface SharedData {}
并使用它
@SharedData
@Value.Immutable
public interface MammalEntity {
public Long getNumberOfLegs();
public Long getNumberOfStomachs();
}
如前所述由Andreas孤店我遇到了一个已知的问题定义这些设置。功能请求#782“不可变对象与构建器的映射”从版本1.1.0.Beta2开始。一个expample可以在mapstruct的集成测试模块中找到。我将在下一次迭代中将这个例子适应于特定的需求。欢迎具体实施的任何建议。 Java-Model-API似乎有点棘手...... –