前提
在开发分层之后,需要对对象进行转换,比如request请求对象转为dto数据库对象。
手动进行对象的转换,虽然执行性能很高,但是开发效率非常低下,且可能会存在漏写的情况。
最近在使用BeanUtils.copyProperties时遇到一个bug。踩坑示例代码:
public class A {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class B {
private String age;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
public class ListA {
private List<A> list;
public List<A> getList() {
return list;
}
public void setList(List<A> list) {
this.list = list;
}
}
public class ListB {
private List<B> list;
public List<B> getList() {
return list;
}
public void setList(List<B> list) {
this.list = list;
}
}
主运行函数
public class BeanUtilsMain {
public static void main(String[] args) {
ListA listA = new ListA();
ArrayList<A> list = new ArrayList<>();
A a = new A();
a.setName("kong");
list.add(a);
listA.setList(list);
ListB listB = new ListB();
BeanUtils.copyProperties(listA,listB);
listB.getList().get(0).setAge("2");
}
}
大家看看会出现什么问题。
从debug可以知道从ListB中拿到的第一个对象类型竟然是A
没仔细看源码,我且称它为浅对象属性复制吧。猜想是因为复制List时,忽视泛型了。
解决
既然出现这种情况了,那必然需要有个解决方案,在推荐之下看到了MapStruct这个框架,写法简单且好用。简单使用案例:
maven
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.3.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.3.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
<scope>provided</scope>
</dependency>
public class A {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class B {
private String age;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
public class ListA {
private List<A> list;
public List<A> getList() {
return list;
}
public void setList(List<A> list) {
this.list = list;
}
}
public class ListB {
private List<B> list;
public List<B> getList() {
return list;
}
public void setList(List<B> list) {
this.list = list;
}
}
代码与上边代码基本一致,只需要再新增一个接口。在编译过程中,MapStruct 将生成该接口的实现。此实现使用纯 Java 的方法调用源对象和目标对象之间进行映射,并非 Java 反射机制。
@Mapper
public interface Converter {
Converter INSTANCE = Mappers.getMapper(Converter.class);
ListB fromSource(ListA source);
ListA toSource(ListB target);
}
主函数
/**
* 参考:https://www.jianshu.com/p/53aac78e7d60
* @author liutong
* @date 2023/1/5 11:35
* @param args
*/
public static void main(String[] args) {
ListA listA = new ListA();
ArrayList<A> list = new ArrayList<>();
A a = new A();
a.setName("kong");
list.add(a);
listA.setList(list);
ListB listB = Converter.INSTANCE.fromSource(listA);
listB.getList().get(0).setAge("2"); // 完全没问题
}
至此结束。
另外MapStruct 提供了 IDEA MapStruct Support 插件,让我们在 IDEA 中,可以更愉快的使用 MapStruct
如何使用请自行百度。
小结
或许BeanUtils.copyProperties有解决方案,聪明的你记得告诉我。
如果使用出错,则添加以下代码试试
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<!-- 引入 mapstruct-processor -->
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<!-- 引入 lombok-processor -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>