在项目中,时常有DTO、VO、BO等转换问题时候,我们会使用Apache或者Spring的BeanUtils来做copy。他们使用的原理都是反射,而且是浅拷贝性能不高。
场景 | 耗时(调用100万次) | 原理 |
---|---|---|
get/set | 20ms | 直接调用 |
MapStruct | 20ms | 接口映射注入(简洁方便) |
BeanCopiers | 20ms | 基于 cglib,修改字节码 |
BeanUtils | 12000ms | 反射 |
PropertyUtils | 4000ms | 反射 |
MapStruct 性能强悍,时间短,通过生成冗长且容易出错的代码来节省时间。在配置方法的约定之后,MapStruct使用了合理的默认值,但在配置或实现特殊行为时将不再适用。
“阿里巴巴编码规范”也有提尽量避免使用BeanUtils工具
使用get/set代码臃肿不够简洁,BeanUtils 、PropertyUtils 性能不够时间长,而BeanCopiers 不过频繁的create太占用资源,降低服务器性能,所以推荐使用MapStruct 来进行对象代码装换。
使用MapStruct
官方查看官网: https://mapstruct.org/
#文档讲解非常详细
使用文档:https://mapstruct.org/documentation/stable/reference/html/
引入依赖
>
>1.3.1.Final >
>
>
<!--lombok 辅助工具-->
org.projectlombok
lombok
1.18.12
provided
org.mapstruct
mapstruct
${mapstruct.version}
org.mapstruct
mapstruct-jdk8
${mapstruct.version}
org.mapstruct
mapstruct-processor
${mapstruct.version}
>
准备几个POJO对象用来相互装换
//课程实体
@Data
@AllArgsConstructor
@Builder
@NoArgsConstructor
public class Course {
private String courseName;
private int sortNo;
private long id;
}
//学生实体
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
private GenderEnum gender;
private Double height;
private LocalDate birthday;
}
//学生响应VO
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StudentVO {
private String name;
private int age;
private String gender;
private Double height;
private String birthday;
private String course;
}
//性别枚举测试装换
@Getter
@AllArgsConstructor
public enum GenderEnum {
Male("1", "男"),
Female("0", "女");
private String code;
private String name;
}
定义一个装换映射器
import org.mapstruct.MapMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import tuan.entity.Course;
import tuan.entity.Student;
import tuan.entity.StudentVO;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* POJO映射装换
* @author DuanLinpeng
* @date 2021/01/09 22:12
**/
@Mapper
public interface ConverMapper {
ConverMapper INSTANCE = Mappers.getMapper(ConverMapper.class);
@Mapping(source = "gender.name", target = "gender")
@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")
StudentVO student2StudentVO(Student student);
/**
* 学生和科目拼接
* @author DuanLinpeng
* @date 2021/01/09 23:33
* @param student
* @param course
* @return tuan.entity.StudentVO
*/
@Mapping(source = "student.gender.name", target = "gender")
@Mapping(source = "student.birthday", target = "birthday", dateFormat = "yyyy-MM-dd ")
@Mapping(source = "course.courseName", target = "course")
@Mapping(target = "name", source = "student.name", defaultValue = "张三")
StudentVO studentAndCourse2StudentVO(Student student, Course course);
/**
* 集合装换成集合
* @author DuanLinpeng
* @date 2021/01/09 23:25
* @param studentList
* @return java.util.List
*/
List<StudentVO> students2StudentVOs(List<Student> studentList);
/**
* Map 装换Map
* @author DuanLinpeng
* @date 2021/01/10 13:11
* @param source
* @return java.util.Map
*/
@MapMapping(valueDateFormat = "dd-MM-yyyy")
Map<String, String> LocalDateMapToStringStringMap(Map<Long, LocalDate> source);
/**
* Set 装换Set<指定类型>
* @author DuanLinpeng
* @date 2021/01/10 13:17
* @param integers
* @return java.util.Set
*/
Set<String> integerSetToStringSet(Set<Integer> integers);
}
测试启动类
public static void main(String[] args) {
//DTO对象装换VO对象
Student student = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(LocalDate.now()).build();
System.out.println(student);
//这行代码便是实际要用的代码
StudentVO studentVO = ConverMapper.INSTANCE.student2StudentVO(student);
System.out.println(studentVO);
//List DTO对象装换List VO对象
Student studentlist = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(LocalDate.now()).build();
List<Student> list = new ArrayList<>();
list.add(studentlist);
List<StudentVO> result = ConverMapper.INSTANCE.students2StudentVOs(list);
System.out.println("List 》》"+result);
//*二个对象拼接到一起
Student studentSplice = Student.builder().name(null).age(6).gender(GenderEnum.Male).height(121.1).birthday(LocalDate.now()).build();
Course courseSplice = Course.builder().id(1L).courseName("语文").build();
StudentVO studentSpliceVO = ConverMapper.INSTANCE.studentAndCourse2StudentVO(studentSplice, courseSplice);
System.out.println("Student And Course 》》"+studentSpliceVO);
//Map值Object 类型装换出自定Map类型
Map<Long, LocalDate> map = new HashMap<>();
map.put(1L,LocalDate.now());
Map<String, String> mapStr = ConverMapper.INSTANCE.LocalDateMapToStringStringMap(map);
System.out.println("Long And LocalDate 》》"+mapStr);
//Set
Set<Integer> set = new HashSet<>();
set.addAll(Arrays.asList(1,2,3,4,5));
Set<String> stringSet = ConverMapper.INSTANCE.integerSetToStringSet(set);
System.out.println("Set To Set 》》" +stringSet);
}
运行结果:
Student(name=小明, age=6, gender=Male, height=121.1, birthday=2021-01-10)
StudentVO(name=小明, age=6, gender=男, height=121.1, birthday=2021-01-10, course=null)
List 》》[StudentVO(name=小明, age=6, gender=男, height=121.1, birthday=2021-01-10, course=null)]
Student And Course 》》StudentVO(name=张三, age=6, gender=男, height=121.1, birthday=2021-01-10 , course=语文)
Long And LocalDate 》》{
1=10-01-2021}
Set<Integer> To Set<String> 》》[1, 2, 3, 4, 5]
还有很多用法请看官方文档:https://mapstruct.org/documentation/stable/reference/html/