由于对 mapstruct 仅会用点皮毛,当时以为碰到了一个诡异的 bug,后面排查发现是由于 default 方法使用不当。
下面还原下场景
@Data
public class User {
private Long id;
private String name;
private String className;
private int age;
private Date arriveTime;
}
converter 类似这样
@Mapper
public interface UserConverter {
@Mapping(target = "className", expression = "java(getClassNameFun(userPO.getClassName()))")
User to(UserPO userPO);
default String getClassNameFun(String className) {
return "光明路小学" + className;
}
}
后面联调发现名称字段被覆盖了,和班级名称一模一样,然后去检查注解是否写错。后续去检查 mapstruct 生成的实现类发现 setName 也调用了 getClassNameFun 方法,所以导致被覆盖。
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2023-02-28T22:44:23+0800",
comments = "version: 1.4.2.Final, compiler: javac, environment: Java 1.8.0_211 (Oracle Corporation)"
)
public class UserConverterImpl implements UserConverter {
@Override
public User to(UserPO userPO) {
if ( userPO == null ) {
return null;
}
User user = new User();
user.setId( userPO.getId() );
user.setName( getClassNameFun( userPO.getName() ) );
user.setAge( userPO.getAge() );
user.setArriveTime( userPO.getArriveTime() );
user.setClassName( getClassNameFun(userPO.getClassName()) );
return user;
}
}
后续查阅 mapstruct 官网 发现,生成实现类的时候,如果有 default methods 会去匹配输入输出的类型,如果相同那么会默认使用这个方法。
有两种方法可以解决这个问题:
(1)对 default 方法加上 @Named 注解
@Mapper
public interface UserConverter {
@Mapping(target = "className", source = "className", qualifiedByName = "getClassNameFun")
User to(UserPO userPO);
@Named("getClassNameFun")
default String getClassNameFun(String className) {
return "光明路小学" + className;
}
}
(2)将 default 方法移到其他类后引入
@Mapper(uses = {ConverterHelper.class})
public interface UserConverter {
@Mapping(target = "className", source = "className", qualifiedByName = "getClassNameFun")
User to(UserPO userPO);
}
@Named("ConverterHelper")
public class ConverterHelper {
@Named("getClassNameFun")
public static String getClassNameFun(String className) {
return "光明路小学" + className;
}
}