MapSturct 是一个生成类型安全,高性能且无依赖的 JavaBean 映射代码的注解处理器(annotation processor)。
- 注解处理器
- 可以生成 JavaBean 之间那的映射代码
- 类型安全,高性能,无依赖性
什么是MapStruct
JavaBean 的困扰
在开发的时候业务代码之间有很多的 JavaBean 之间的相互转化, 非常的影响观感,却又不得不存在。
后来想的一个办法就是通过反射,或者自己写很多的转换器。
第一种通过反射的方法确实比较方便,但是现在无论是 BeanUtils
,BeanCopier
等在使用反射的时候都会影响到性能。
虽然可以进行反射信息的缓存来提高性能,但是像这种的话,需要类型和名称都一样才会进行映射,有很多时候,由于不同的团队之间使用的名词不一样,还是需要很多的手动 set/get 等功能。
第二种的话就是会很浪费时间,而且在添加新的字段的时候也要进行方法的修改,不过,由于不需要进行反射,其性能是很高的。
MapStruct 带来的改变
MapSturct 是一个生成类型安全,高性能且无依赖的 JavaBean 映射代码的注解处理器(annotation processor)。
- 注解处理器
- 可以生成 JavaBean 之间那的映射代码
- 类型安全,高性能,无依赖性
MapStruct 入门
添加依赖
1 | <dependency> |
po类
1 |
|
dto类
1 |
|
创建转换接口
1 | // 可以使用abstract class代替接口 |
测试方法
1 |
|
MapStruct优点分析
性能高
这是相对反射来说的,反射需要去读取字节码的内容,花销会比较大。
而通过 MapStruct 来生成的代码,其类似于人手写。速度上可以得到保证。
使用简单
如果是完全映射的,使用起来肯定没有反射简单。用类似 BeanUtils 这些工具一条语句就搞定了。
但是,如果需要进行特殊的匹配(特殊类型转换,多对一转换等),其相对来说也是比较简单的。
基本上,使用的时候,只需要声明一个接口,接口下写对应的方法,就可以使用了。
当然,如果有特殊情况,是需要额外处理的。
代码独立
生成的代码是对立的,没有运行时的依赖。
易于 debug
在生成的代码中,可以轻易的进行 debug。
MapStruct使用案例
属性名称相同
在实现类的时候,如果属性名称相同,则会进行对应的转化。
通过此种方式,可以快速的编写出转换的方法。(入门案例)
属性名不相同
属性名不相同,在需要进行互相转化的时候,则可以通过@Mapping
注解来进行转化。
1 |
|
- source 需要转换的对接,通常是入参
- target 转换的对接,通常是出参
- ignore 忽略,默认false不忽略,需要忽略设置为true
- defaultValue 默认值
- expressions 可以通过表达式来构造一些简单的转化关系。虽然设计的时候想兼容很多语言,不过目前只能写Java代码。
1 | ({ |
这里用到演示了如何使用TimeAndFormat
对time
和format
操作,这里必须要指定需要使用的Java类的完整包名,不然编译的时候不知道使用哪个Java类,会报错。
1 |
|
转换非基础类型属性
如果subUser与subUserDto字段名称相同直接配置即可完成(对象类型,包括list)
1 |
|
Mapper 中使用自定义的转换
有时候,对于某些类型,无法通过代码生成器的形式来进行处理。那么, 就需要自定义的方法来进行转换。这时候,可以在接口(同一个接口,后续还有调用别的 Mapper 的方法)中定义默认方法(Java8及之后)。
1 |
|
只能存在一个default
修饰的方法
1 |
|
多转一
在实际的业务中少不了将多个对象转换成一个的场景。MapStruct 当然也支持多转一的操作。
1 |
|
遵循原则
- 当多个对象中, 有其中一个为 null, 则会直接返回 null
- 如一对一转换一样, 属性通过名字来自动匹配。因此, 名称和类型相同的不需要进行特殊处理
- 当多个原对象中,有相同名字的属性时,需要通过
@Mapping
注解来具体的指定, 以免出现歧义(不指定会报错)。如上面的 name
属性也可以直接从传入的参数来赋值
1 | "person.description", target = "description") (source = |
更新 Bean 对象
有时候,不是想返回一个新的 Bean 对象,而是希望更新传入对象的一些属性。这个在实际的时候也会经常使用到。
1 |
|
map映射
1 | "yyyy-MM-dd HH:mm:ss") (valueDateFormat = |
多级嵌套
只需要在mapper接口中定义相关的类型转换方法即可,list类型也适用
方式1
1 |
|
方式2
通过uses配置类型转换
1 | .class}) (uses = {TestMapper |
获取 mapper
通过 Mapper 工厂获取
都是通过 Mappers.getMapper(xxx.class)
的方式来进行对应 Mapper 的获取。
此种方法为通过 Mapper 工厂获取。
如果是此种方法,约定俗成的是在接口内定义一个接口本身的实例 INSTANCE
, 以方便获取对应的实例。
1 |
|
这样在调用的时候,就不需要在重复的去实例化对象了。类似下面
1 | Target target = SourceMapper.INSTANCE.source2target(source); |
使用依赖注入
对于 Web 开发,依赖注入应该很熟悉。MapSturct 也支持使用依赖注入,同时也推荐使用依赖注入。
1 | "spring") (componentModel = |
依赖注入策略
可以选择是通过构造方法或者属性注入,默认是属性注入。
1 | public enum InjectionStrategy { |
类似如此使用
1 | "cdi" injectionStrategy = InjectionStrategy.CONSTRUCTOR) (componentModel = |
自定义类型转换
有时候,在对象转换的时候可能会出现这样一个问题,就是源对象中的类型是Boolean类型,而目标对象类型是String类型,这种情况可以通过@Mapper
的uses属性来实现:
1 |
|
要注意的是,如果使用了例如像spring这样的环境,Mapper引入uses类实例的方式将是自动注入,那么这个类也应该纳入Spring容器
1 |
|