是什么?
一个java注释处理器,用于生成类型安全、高性能和无依赖关系的 bean 映射代码。
能做什么?
- 对象的映射,如user映射为userDto。
- 对象字段的转换,如applyType=1,转换为ipTypeCn=创建。
怎么用?
以MapStruct 1.5.2.Final版本为例
-
引入依赖
<dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.5.2.Final</version> </dependency>
-
build
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.5.2.Final</version> </path> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> </path> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok-mapstruct-binding</artifactId> <version>0.2.0</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>
-
代码步骤
-
定义接口或者抽象类实现映射器,并增加对应注解
-
定义映射方法
-
如:
package cn.cnn.bean.mapstruct; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; /** * @author ningning.cheng * @since 2022/8/28 **/ @Mapper public interface StudentConverter { StudentConverter INSTANCE = Mappers.getMapper(StudentConverter.class); /** * <pre> * 1. 当一个属性与其对应的目标实体同名时,它将被隐式映射。 * 2. 当一个属性在目标实体中具有不同的名称时,可以通过@Mapping注解指定其名称。 * </pre> * * @param student * @return */ @Mapping(target = "studentName", source = "name") StudentDto student2StudentDto(Student student); }
//生成方法 @Override public StudentDto student2StudentDto(Student student) { if ( student == null ) { return null; } StudentDto studentDto = new StudentDto(); studentDto.setStudentName( student.getName() ); studentDto.setAge( student.getAge() ); return studentDto; }
-
-
用法举例
实体类:
package cn.cnn.bean.mapstruct; import lombok.Data; /** * @author ningning.cheng * @since 2022/8/28 **/ @Data public class Student { private String name; private Integer age; private Course course; public Student() {} public Student(String name, Integer age) { this.name = name; this.age = age; } }
package cn.cnn.bean.mapstruct; import lombok.Data; /** * @author ningning.cheng * @since 2022/8/28 **/ @Data public class StudentDto { private String studentName; private Integer age; private String courseName; }
-
多源参数映射
// map参数 Map<String, String> map = new HashMap<>(); map.put("name", "ww"); StudentDto studentDto1 = StudentConverter.INSTANCE.map2StudentDto(map); System.out.println(studentDto1);
// 映射方法内容 @Mapping(target = "studentName", source = "name") StudentDto map2StudentDto(Map<String, String> map);
//生成的代码 @Override public StudentDto map2StudentDto(Map<String, String> map) { if ( map == null ) { return null; } StudentDto studentDto = new StudentDto(); if ( map.containsKey( "name" ) ) { studentDto.setStudentName( map.get( "name" ) ); } if ( map.containsKey( "age" ) ) { studentDto.setAge( Integer.parseInt( map.get( "age" ) ) ); } return studentDto; }
-
嵌套映射
// 嵌套 StudentDto studentDto2 = StudentConverter.INSTANCE.nested2StudentDto(student); System.out.println(studentDto2);
// 定义方法 @Mapping(target = "courseName", source = "course.name") StudentDto nested2StudentDto(Student student);
// 生成代码 @Override public StudentDto nested2StudentDto(Student student) { if ( student == null ) { return null; } StudentDto studentDto = new StudentDto(); studentDto.setCourseName( studentCourseName( student ) ); studentDto.setAge( student.getAge() ); return studentDto; } private String studentCourseName(Student student) { if ( student == null ) { return null; } Course course = student.getCourse(); if ( course == null ) { return null; } String name = course.getName(); if ( name == null ) { return null; } return name; }
-
映射集合
// 集合 List<StudentDto> studentDto3 = StudentConverter1.INSTANCE.students2StudentDtos(Lists.newArrayList(student)); System.out.println(studentDto3);
package cn.cnn.bean.mapstruct; import java.util.List; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; /** * @author ningning.cheng * @since 2022/8/28 **/ @Mapper public interface StudentConverter1 { StudentConverter1 INSTANCE = Mappers.getMapper(StudentConverter1.class); List<StudentDto> students2StudentDtos(List<Student> students); }
//生成代码 @Override public List<StudentDto> students2StudentDtos(List<Student> students) { if ( students == null ) { return null; } List<StudentDto> list = new ArrayList<StudentDto>( students.size() ); for ( Student student : students ) { list.add( studentToStudentDto( student ) ); } return list; } protected StudentDto studentToStudentDto(Student student) { if ( student == null ) { return null; } StudentDto studentDto = new StudentDto(); studentDto.setAge( student.getAge() ); return studentDto; }
-
高级用法
- 默认值和常量
@Mapper public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mapping(target = "stringProperty", source = "stringProp", defaultValue = "undefined") @Mapping(target = "longProperty", source = "longProp", defaultValue = "-1") @Mapping(target = "stringConstant", constant = "Constant Value") @Mapping(target = "integerConstant", constant = "14") @Mapping(target = "longWrapperConstant", constant = "3001") @Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014") @Mapping(target = "stringListConstants", constant = "jack-jill-tom") Target sourceToTarget(Source s); }
- 表达式
@Mapper public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mapping(target = "timeAndFormat", expression = "java( new org.sample.TimeAndFormat( s.getTime(), s.getFormat() ) )") Target sourceToTarget(Source s); }
imports org.sample.TimeAndFormat; @Mapper( imports = TimeAndFormat.class ) public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mapping(target = "timeAndFormat", expression = "java( new TimeAndFormat( s.getTime(), s.getFormat() ) )") Target sourceToTarget(Source s); }
- 默认表达式
imports java.util.UUID; @Mapper( imports = UUID.class ) public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mapping(target="id", source="sourceId", defaultExpression = "java( UUID.randomUUID().toString() )") Target sourceToTarget(Source s); }
总结
- 与动态映射框架相比,MapStruct 具有以下优点:
- 通过使用普通方法调用而不是反射来快速执行
- 编译时类型安全:只能映射相互映射的对象和属性。
- 在构建时清除错误报告,如果
- 映射不完整(并非所有目标属性都已映射)
- 映射不正确(找不到合适的映射方法或类型转换)
- 缺点:
- 通过在开发中生成源代码的方式实现,所以对于动态对象数据拷贝并不适合。
参考
https://mapstruct.org