kl个人博客 首页>>java>>一个巧合踩了MapStruct表达式的坑

一个巧合踩了MapStruct表达式的坑

一个巧合踩了MapStruct表达式的坑

前言

一不小心踩了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接口中移出去就好了。


kl个人博客