我们在实际开发过程中会出现很多bean之间的拷贝动作,这样的动作需要不停的去进行set操作。容易造成代码的耦合和维护困难。通过MapStruct我们只需要定义一个接口,这个工具包会帮我们自动生成一个实现类,并且这个类是不可编辑的,代码自动生成帮我们省去了开发维护成本同时做了一定的解耦。
Maven依赖
使用MapStruct我们需要通过maven导入相关的依赖:
1 | <dependency> |
同时我们需要添加maven-compiler-plugin,其中mapstruct-processor用于构建mapper实现类的生成器。
1 | <plugin> |
基本的Mapping用法
定义一个Java的POJO类和Mapper Interface:
1 | public class SimpleSource { |
1 |
|
注意到我们这里并没有去实现这个接口,但是MapStruct帮我们生成了一个实现类。我们可以通过调用mvn compile或者通过mvn clean install调用MapStruct,它会帮我们生成一个Mapper的实现类在/target/generated-sources/annotations/目录下。这里贴上一段,注意这个类是不可编辑的,如果编辑这个类,再重新执行编译之后也会被替换掉:
1 | public class SimpleSourceDestinationMapperImpl |
执行一个测试用例:
1 | public class SimpleSourceDestinationMapperTest { |
通过依赖注入的方式获取Mapper服务
我们一般情况下可以通过Mappers.getMapper(YourClass.class)的方式获取到一个Mapper实例,然后调用到具体的mapper方法,我们也可以注入的方式获取到我们的实例:
1 | "spring") (componentModel = |
在interface上加了这个属性之后,我们可以通过@Autowire的方式在任何地方引用这个实例。
映射不同的字段名
针对下面的POJOs我们可以定义另一个Mapper:
1 | public class EmployeeDTO { |
1 |
|
映射对象字段
如果这边两个类之间的映射中还有嵌套的对象,我们在同一个Mapper的接口中定义这两个嵌套对象的转换规则。
1 | public class EmployeeDTO { |
修改之后的Mapper接口为:
1 | DivisionDTO divisionToDivisionDTO(Division entity); |
测试用例:
1 |
|
字段类型转换
如果我们需要将一个String类型映射为Date类型的字段,定义一个dateFormat,同时还提供了expression等属性:
1 | ({ |
@Mappping的注解我们还可以定义一些属性,defaultExpression和expression可以定义一些简单的表达式,直接传递给实现方法,但是不可能引用一些工具类,只要import的方法都没办法自动导入,这个比较鸡肋。这里贴一些简单的:
1 | //这个会将java.util.UUID.randomUUID().toString()这个方法直接引用在目标字段上。 |
同时我们可能也可以这样用:
1 | "spring") (componentModel = |
其实这个感觉还是比较鸡肋的,如果可以支持比较复杂的一些转换实际场景可能更受用。
进行复杂转换
主要在实际的应用场景中,需要输入转换的参数可能是多个,同时我们还需要一些自定义的部分,通过加@BeforeMapping等注解可以将自定义的部分嵌入到实现类中,这里也可以支持lambok,有些版本可能会报错,maven的版本需要在3.6.0以上:
1 | //两个POJOs |
需要定义为抽象类,这里的寓意是限制性第一个方法,在执行第二个方法,第一个方法中可以加一些复杂的参数转换,第二个方法做一些通用的处理,同时需要使用@MappingTarget注解保证输出对象的唯一引用,不加默认会帮你重新new一个出来并且return:
1 | "spring") (componentModel = |
总结
这里为什么要介绍MapStruct这个工具包,实际上在我们的项目中确实有太多的拷贝动作,导致了方法都是在set,get方法过长,较难进行维护,非常不清晰。之后可以尝试通过MapStruct改造一些有类似问题的接口,通过定义一个抽象类的Mapper,将复杂操作维护一个自定义方法,简单的映射直接通过@Mapping,开发者只需要关心对应的抽象接口定义。