前言
一不小心踩了MapStruct表达式的坑,发现了一个在官方文档上都找不到的功能,有必要记录下。MapStruct是一个代码生成器,它基于约定优于配置的方法大大简化了Java Bean类型之间的映射的实现。生成的映射代码使用简单的方法调用,因此速度快,类型安全且易于理解。MapStruct的表达式功能是为了处理特殊对象属性的映射问题,比如DTO中的status属性转换成PO中的status需要进一步的处理,这个时候就需要用到表达式功能了。这里不再赘述关于MapStruct的使用问题,更多的使用教程可参考文档
MapStruct的中文译文文档:http://www.kailing.pub/MapStruct1.3/index.html#defining-mapper
遇到的问题先看一段映射的代码:
@Mapper(imports = CustomProcessors.class) public interface DepartmentsMapper { @Mapping(target = "status", expression = "java( DepartmentsMapper.toStatus(department.getStatus()) )") DepartmentsVO boToVo(DepartmentBO department); static String toStatus(String status){ return status + "状态"; } }原本这段代码表达的语义是:将DepartmentBO的status属性赋值给DepartmentsVO时,需要进行一些简单的转换,转换的方法就是Mapper接口里定义的toStatus静态方法。可最终MapStruct生成的代码把其他属性的赋值动作也做了处理。生成的代码如下:
可以看到除了指定的status属性加上了表达式中的代码,其他的属性也都加上了,这不是我们想要的效果。
发现原因
楼主反复查看官方文档,最后就差把源码拉下来看实现逻辑了,最后一个闪念猜想到了可能的原因。上面关于表达式的使用代码没有问题,官方文档也写的很清楚。主要是因为这里触发了MapStruct的一个隐藏功能:仔细观察生成的代码发现,只有Integer属性的字段加上表达式中的代码了,楼主推断只要在定义maping的接口中定义了转换方法就会被自动应用到相同类型属性的转换上。后面验证确实如此,比如我去掉表达式的定义:
@Mapper(imports = CustomProcessors.class) public interface DepartmentsMapper { DepartmentsVO boToVo(DepartmentBO department); static Integer toStatus(Integer status){ return status + 1; } }最终生成的代码还是所有的Integer属性的值都加上了toStatus的处理了
结语
最后发现的这个特性竟然在官方文档上找不到丝毫的描述,其实可以算一个非常不错的功能,可以统一处理相同类型的属性,比如属性是一个对象时,只是在博主的这个场景下使用不到。最后的解决方案是将表达式中的代码定义从Mapper接口中移出去就好了。