类型转换常见的方式
一般类型转换常用的两种方式:
1、BeanUtils(Spring、Apache、Hutool)各种实现都有,相对来说速度略慢;
2、MapStruct(速度相比BeanUtil快)。
BeanUtils的坑
接上,Spring BeanUtils之前遇到过一个最大的坑就是用copyProperties()方法,如果source和target中,某个属性的类型不一致,该属性就会被忽略。
但很多时候,这个问题很容易被忽略。
所以,用BeanUtils的时候一定要注意这个问题。
MapStruct的坑
相比BeanUtils,MapStruct提升了处理速度。
异常提示很友好,如果两个字段类型不一致,MapStruct会抛出异常。
一目了然,就会去填坑了。
MapStruct的坑-续
假设有下面这样一段代码:
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface OrderConvert {
OrderConvert INSTANCE = Mappers.getMapper(OrderConvert.class);
@Mappings(
value = {
@Mapping(source = "conName", target = "name"),
@Mapping(source = "orgCode", target = "orgId"),
@Mapping(
target = "payFee",
expression = "java(payFeeFormat(orderVo.getConMoney()))"),
})
ThirdOrderVo toThirdOrderVo(ConOrderVo orderVo);
default String payFeeFormat(String number) {
var multiplied = NumberUtil.mul(Double.parseDouble(number), 100);
return NumberUtil.round(multiplied, 0).toPlainString();
}
}
预期应该是:
只有payFee属性应用了payFeeFormat方法。
实际是,所有类型为String的属性都应用了payFeeFormat方法,导致payFeeFormat方法里面的转换double抛出异常(不抛异常得多久才能发现...)。
MapStruct填坑
那怎么处理?
目前我采用了修改payFeeFormat的入参,比如我在expression中提前将number转换为double类型。
修改后的片段:
@Mappings(
value = {
@Mapping(source = "conName", target = "name"),
@Mapping(source = "orgCode", target = "orgId"),
@Mapping(
target = "payFee",
expression = "java(payFeeFormat(Double.parseDouble(orderVo.getConMoney())))"),
})
ThirdOrderVo toThirdOrderVo(ConOrderVo orderVo);
default String payFeeFormat(double number) {
var multiplied = NumberUtil.mul(number, 100);
return NumberUtil.round(multiplied, 0).toPlainString();
}
目前看是能正常使用了,假如有多个double类型字段怎么办?
别走!转角遇到惊喜
千辛万苦(千载难逢),在官方找到了这个issue(①):
具体解决方案就是,在自定义方法上面增加@Named注解,仅限特定属性使用就可以了。
完整代码如下:
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
@Mapper
public interface OrderConvert {
@Mappings(
value = {
@Mapping(source = "conName", target = "name"),
@Mapping(source = "orgCode", target = "orgId"),
@Mapping(
target = "payFee",
expression = "java(payFeeFormat(orderVo.getConMoney()))"),
})
ThirdOrderVo toThirdOrderVo(ConOrderVo orderVo);
@Named("payFee")
default String payFeeFormat(double number) {
var multiplied = NumberUtil.mul(number, 100);
return NumberUtil.round(multiplied, 0).toPlainString();
}
}
这个办法好,Java好啊,Java还是得学啊。
[手动脑补表情图(英语好啊,这英语得学)]
总结一下,根据issue的内容:
官方认为,如果你定义了一个入参及返回值,与某个属性一致的方法,MapStruct就会"物尽其用"。
只是,这"物尽其用"让人有点哭笑不得😂
①:官方issue
https://github.com/mapstruct/mapstruct/issues/3548