一、背景
在 Java 开发中,bean 对象的映射转换或者拷贝的应用场景是极其常见的需求。无论是java 软件体系中经典的MVC式的三层架构,还是DDD领域驱动式的架构,其总会有各种DTO、DO、PO、VO之间的转换需求,又或者在软件平台对外接口实体向内层算法库(和平台基于同一语言开发)传递时,两层之间的对象实体参数由于各种原因无法完全一致,需要做对象协议转换等场景。面对这种实体转换的场景,其实最简单的是手工的方式(直接get/set)进行对象字段重新转换赋值,这种方式占用的内存开销最小,但是这种方法也有致命弱点,不具有抽象性、通用性,换一种场景则需要重新硬编码,耗费时间和人力,如果是字段数很少的实体赋值转换建议采用这种方式,如果字段很多或者需要完成任意类型转换,这种弊端就凸显出来了。因此,如何高效、灵活地实现对象映射是开发人员需要解决的问题,本文将对比分析常见的对象映射工具,比较其优缺点,然后再介绍一下其中有效工具MapStruct的使用方式,最后进入到源码中分析一下它的实现原理。
二、常见的对象映射工具
Java 生态中,有多个开源工具和框架可以完成对象映射任务。以下是主流工具的概述及对比。
1. Spring BeanUtils
Spring 提供的 BeanUtils 是最基础的工具,支持简单属性拷贝。
特点:
开箱即用,无需额外依赖,语法简单,便于快速实现对象转换,仅支持同名同类型的简单属性拷贝,不支持嵌套映射和复杂转换逻辑,性能低。
2.Apache Commons BeanUtils
Apache Commons 提供的 BeanUtils 是一个经典的映射工具,功能稍强于 Spring 的 BeanUtils。
- 特点: 支持 Map 和 JavaBean 的相互转换,嵌套对象需要手动处理,性能较差,依赖反射实现。
3. Dozer
Dozer 是一款功能强大的对象映射框架,支持 XML 配置和深度映射。
特点:
支持复杂嵌套对象和深拷贝,但基于反射实现,性能较低。
4. BeanCopier
BeanCopier是一款支持不同类型数据之间转换的开源工具。
特点:
支持不同数据类型之间的转换,但是不支持不同字段名之间的转换。
5.MapStruct
MapStruct 是目前性能最优的对象映射工具之一,基于编译时代码生成而实现。
- 特点: 性能极高(无运行时开销),强类型检查,编译期发现错误,配置灵活,支持复杂规则和字段映射。
6.ModelMapper
ModelMapper 是一个易用、灵活的对象映射工具,支持动态和复杂的映射需求。
- 特点: 自动映射字段,基于反射,性能稍逊于MapStruct。
7.Orika
Orika 是一款性能较高的映射工具,支持复杂映射规则和动态生成。
- 特点: 基于javassist代理实现,效率高,支持深拷贝、嵌套对象映射,但是配置比较复杂度,学习成本略高,文档和社区支持弱。
8.Selma
Selma 是一个轻量级工具,基于注解的代码生成,功能类似 MapStruct,也是作用于编译时。
- 特点: 性能高,编译时生成代码,配置简单,易于上手,但是功能比较简单,不适合复杂场景。
三、工具对比与建议
其实在github 上已经有人对比了不同bean转换工具之间的性能,链接在这里:https://github.com/arey/java-object-mapper-benchmark,对比结果如下图所示:

选择建议:
从上面可以看出MapStruct和Selma 性能和Manual 方式调用get set 效率大体相当。而MapStruct和Selma都是基于JSR 269的Java注解处理器实现的,作用时机在编译阶段,因此不会影响运行时速度,由于从对比结果来看MapStruct效果最好,因此下文将详细介绍MapStruct的使用。
四、MapStruct介绍及使用
4.1 MapStruct简介
MapStruct是一个Java注解处理器 ,它的主要功能是自动生成类型安全、高性能且无依赖的bean映射代码。这个工具基于“约定优于配置”的原则,极大地简化了Java Bean类型之间的映射实现过程。而根据上文的对比发现:MapStruct作用于Java代码编译时期,不会在运行时进行反射操作,其进行对象转换时速度快。
4.2 MapStruct Maven引入
在pom.xml文件添加MapStruct 相关依赖,其相关依赖主要由两个包组成:
org.mapstruct:mapstruct: 包含了映射相关的注解,如@Mapper、@Mapping等。
org.mapstruct:mapstruct-processor: 包含了注解处理器。用于处理注解相关的逻辑,如MappingProcessor等。
<!--mapStruct依赖 高性能对象映射-->
<!--mapstruct核心-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
<!--mapstruct编译-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
</dependency>
4.3 MapStruct基础使用
创建MapStruct 映射步骤总结:
1. 新增转换接口或抽象类,并且使用@Mapper注解(org.mapstruct.Mapper)标签修饰。
2. 添加自定义转换方法。
3. 调用自定义转换方法。
4.3.1 MapStruct基本映射
// User类
@Data
@Builder
public class User {
String describe;
private String id;
private String name;
private int age;
private BigDecimal source;
private double height;
private Date createTime;
}
// UserDTO类
@Data
public class UserDTO {
String describe;
private Long id;
private String personName;
private String age;
private String source;
private String height;
}
//UserCovertBasic 映射接口
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = "spring")
public interface UserCovertBasic {
UserCovertBasic INSTANCE = Mappers.getMapper(UserCovertBasic.class);
UserDTO userToUserDTO(User user);
}
// 测试主类
public class TestMapStruct {
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM");
User user = User.builder().id("1").name("张三").age(18)
.source(new BigDecimal("100"))
.height(1.88)
.describe("java开发")
.createTime(format.parse("2025-05"))
.build();
UserDTO userDTO = UserCovertBasic.INSTANCE.userToUserDTO(user);
System.out.println(userDTO);
}
}
执行效果如下:
UserDTO(describe=java开发, id=1, personName=null, age=18, source=100, height=1.88)
优化:User类实例对象 转换UserDTO类实例对象发现 User.name 属性无法与UserDTO.personName 属性无法实现一一对应,可以通过@Mappering 注解标签实现不同属性名称转换。
@Mapper(componentModel = "spring")
public interface UserCovertBasic {
UserCovertBasic INSTANCE = Mappers.getMapper(UserCovertBasic.class);
@Mapping(source = "name", target = "personName")
UserDTO userToUserDTO(User user);
}
执行效果如下:
UserDTO(describe=java开发, id=1, personName=张三, age=18, source=100, height=1.88)
注意:
- 当一个属性与其目标实体对应的名称相同时,它将被隐式映射。
- 当属性在目标实体中具有不同的名称时,可以通过@Mapping注解指定其名称。
- 如果忽略映射字段加@Mapping(target = “字段名称”, ignore = true)
4.3.2 MapStruct指定默认值
功能要求:User类实例对象 转换UserDTO类实例对象时,将describe属性值设置为:“默认值”。
@Mapping(source = "describe", target = "describe", defaultValue = "默认值")
public class TestMapStruct {
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
User user = User.builder().id("1").name("张三").age(18)
.source(new BigDecimal("100"))
.height(1.88)
//.describe("java开发")
.createTime(format.parse("2025-05-08"))
.build();
UserDTO userDTO = UserCovertBasic.INSTANCE.userToUserDTO(user);
System.out.println(userDTO);
//UserDTO(describe=默认值, id=1, personName=张三, age=18, source=100, height=1.88)
}
}
4.3.3 MapStruct表达式
功能要求:在UserDTO类中添加时间属性currentDay,并且要求赋值系统当前时间。那么需要在@Mapping注解标签中必须添加target属性和expression属性。
@Data
public class UserDTO {
String describe;
private Long id;
private String personName;
private String age;
private String source;
private String height;
private Date currentDay;
}
@Mapper(componentModel = "spring")
public interface UserCovertBasic {
UserCovertBasic INSTANCE = Mappers.getMapper(UserCovertBasic.class);
@Mapping(source = "name", target = "personName")
@Mapping(source = "describe", target = "describe", defaultValue = "默认值")
@Mapping(target = "currentDay", expression = "java(new java.util.Date())")
UserDTO userToUserDTO(User user);
}
执行结果:UserDTO(describe=默认值, id=1, personName=张三, age=18, source=100, height=1.88, currentDay=Thu May 08 18:37:37 CST 2025)
注意:expression()属性不能与source()、defaultValue()、defaultExpression()、qualifiedBy()、qualifiedByName()或constant()在同一个@mapping注解中一起使用。
默认表达式@Mapping#defaultExpression()是默认值和表达式的组合。仅当source属性为null时才使用它们。
4.3.4 MapStruct时间格式
功能要求:指定UserDTO类属性currentDay的时间格式为’yyyy-MM-dd’。那么需要在@Mapping注解标签中必须添加target属性、source属性 和dateFormat属性。
@Mapper(componentModel = "spring")
public interface UserCovertBasic {
UserCovertBasic INSTANCE = Mappers.getMapper(UserCovertBasic.class);
@Mapping(source = "name", target = "personName")
@Mapping(source = "describe", target = "describe", defaultValue = "默认值")
@Mapping(source = "createTime", target = "currentDay", dateFormat = "yyyy-MM-dd")
UserDTO userToUserDTO(User user);
}
public class TestMapStruct {
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy");
User user = User.builder().id("1").name("张三").age(18)
.source(new BigDecimal("100"))
.height(1.88)
//.describe("java开发")
.createTime(format.parse("2025"))
.build();
UserDTO userDTO = UserCovertBasic.INSTANCE.userToUserDTO(user);
System.out.println(userDTO);
}
}
执行效果如下:
UserDTO(describe=默认值, id=1, personName=张三, age=18, source=100, height=1.88, currentDay=Wed Jan 01 00:00:00 CST 2025)
4.3.5 MapStruct数字格式
功能要求:指定UserDTO类属性age的数字格式为’#0.00’。那么需要在@Mapping注解标签中必须添加target属性、source属性 和numberFormat属性。
执行效果如下:
UserDTO(describe=默认值, id=1, personName=张三, age=18.00, source=100, height=1.88, currentDay=Wed Jan 01 00:00:00 CST 2025)
4.4 MapStruct进阶使用
4.4.1 多参数源映射🌟
功能要求:新增一个类对象(BasicEntity),要求将BasicEntity中的createTime 属性赋值给UserDTO 类中的currentDay属性。
@Data
@Builder
public class BasicEntity {
private Date createTime;
private String createBy;
private Date updateTime;
private String updateBy;
private int _ROW;
}
@Mapper(componentModel = "spring")
public interface UserCovertBasic {
UserCovertBasic INSTANCE = Mappers.getMapper(UserCovertBasic.class);
@Mapping(source = "name", target = "personName")
@Mapping(source = "describe", target = "describe", defaultValue = "默认值")
// @Mapping(target = "currentDay", expression = "java(new java.util.Date())")
@Mapping(source = "createTime", target = "currentDay", dateFormat = "yyyy-MM-dd")
@Mapping(source = "age", target = "age", numberFormat = "#0.00")
UserDTO userToUserDTO(User user);
/**
* 多源映射转换
* @param user
* @param basicEntity
* @return
*/
@Mapping(source = "basicEntity.createTime", target = "currentDay")
@Mapping(source = "user.name", target="personName")
UserDTO userBasicToUserDTO(User user,BasicEntity basicEntity);
}
public class TestMapStruct {
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy");
User user = User.builder().id("1").name("张三").age(18)
.source(new BigDecimal("100"))
.height(1.88)
//.describe("java开发")
.createTime(format.parse("2025"))
.build();
BasicEntity basicEntity = BasicEntity.builder()
.createTime(new Date())
.createBy("索隆")
.updateTime(new Date())
.updateBy("乌索普")
._ROW(1)
.build();
UserDTO userDTO = UserCovertBasic.INSTANCE.userBasicToUserDTO(user,basicEntity);
System.out.println(userDTO);
}
}
//UserDTO(describe=null, id=1, personName=张三, age=18, source=100, height=1.88, currentDay=Thu May 08 18:52:05 CST 2025)
功能要求:UserDTO类中的id 属性值要求从方法中传入,不接受转换类User中的id 属性。那么需要在UserMapper接口新增其他参数值映射接口定义。
@Mapping(source = "user.name", target="personName")
@Mapping(target = "id", source = "id")
UserDTO mapTo(User user, String id);
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy");
User user = User.builder().id("1").name("张三").age(18)
.source(new BigDecimal("100"))
.height(1.88)
//.describe("java开发")
.createTime(format.parse("2025"))
.build();
UserDTO userDTO = UserCovertBasic.INSTANCE.mapTo(user,"666");
System.out.println(userDTO);
}
//UserDTO(describe=null, id=666, personName=张三, age=18, source=100, height=1.88, currentDay=null)
4.4.2 嵌套映射🌟
嵌套映射这个场景其实非常重要,因为平时工作中很多时候就是类中嵌套着另一个类。
功能要求:为 User类新增一个类属性Company 公司属性,为UserDTO类新增一个类型属性CompanyDTO公司属性。
@Data
public class Company {
public String name;
public String address;
public Integer numberOfPeople;
public Date createDate;
public String createBy;
}
@Data
public class CompanyDTO {
public String name;
public String address;
public Integer numbers ;
public Date currentDate;
public String leader;
}
@Data
@Builder
public class User {
String describe;
private String id;
private String name;
private int age;
private BigDecimal source;
private double height;
private Date createTime;
private Company company;
}
@Data
public class UserDTO {
String describe;
private Long id;
private String personName;
private String age;
private String source;
private String height;
private Date currentDay;
private CompanyDTO dto;
}
/**
* 嵌套类属性转换(covertNestedProperties和convertDto都是需要在UserMapper接口里新增的方法)
* @param person
* @return
*/
@Mapping(source="person.company", target = "dto")
UserDTO covertNestedProperties(User person);
@Mapping(source = "createBy", target = "leader")
@Mapping(source = "createDate", target = "currentDate")
@Mapping(source = "numberOfPeople", target = "numbers")
CompanyDTO convertDto(Company company);
// 测试主类
public class MapStructStudyTest {
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy");
Company company = Company.builder().address("新世界").createDate(format.parse("1997")).createBy("路飞")
.numberOfPeople(4).name("草帽海贼团").build();
User user = User.builder().age(31).createTime(format.parse("1997")).id("1").describe("Java 开发")
.height(185L).name("路飞").source(new BigDecimal(10000)).company(company).build();
UserDTO userDTO = UserCovertBasic.INSTANCE.covertNestedProperties(user);
System.out.println(userDTO);
}
}
//UserDTO(describe=Java 开发, id=1, personName=null, age=31, source=10000, height=185.0, currentDay=null, dto=CompanyDTO(name=草帽海贼团, address=新世界, numbers=4, currentDate=Wed Jan 01 00:00:00 CST 1997, leader=路飞))
拓展:嵌套属性为集合属性对象时,MapStruct 如何完成转换?
功能要求:为 User 类新增一个集合属性List 房屋属性,为UserDTO类新增一个Lis房屋属性。那么做法如下:
// 1.先新增两个model类:House、HouseDTO:
@Data
@Builder
public class House {
private String address;
private Integer price;
}
@Data
public class HouseDTO {
private Integer house_pay;
private String address;
}
// 2.再在User和UserDTO分别添加相关集合属性:
private List<House> houses;
private List<HouseDTO> houseDTOs;
// 3.然后在UserMapper接口里新增如下方法
@Mapping(source="person.company", target = "dto")
@Mapping(source = "person.houses", target ="houseDTOs")
UserDTO covertNestedColl(User person);
List<HouseDTO> covertColl(List<House> houses);
@Mapping(source = "price", target = "house_pay")
HouseDTO convertHouse(House house);
// 4.在测试主类里进行测试
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy");
Company company = Company.builder().address("新世界").createDate(format.parse("1997")).createBy("路飞")
.numberOfPeople(4).name("草帽海贼团").build();
House house = House.builder().address("新世界").price(10000).build();
User user = User.builder().age(31).createTime(format.parse("1997")).id("1").describe("Java 开发")
.height(185L).name("路飞").source(new BigDecimal(10000))
.company(company).houses(Arrays.asList(house)).build();
UserDTO userDTO = UserCovertBasic.INSTANCE.covertNestedColl(user);
System.out.println(userDTO);
}
//UserDTO(describe=Java 开发, id=1, personName=null, age=31, source=10000, height=185.0, currentDay=null, dto=CompanyDTO(name=草帽海贼团, address=新世界, numbers=4, currentDate=Wed Jan 01 00:00:00 CST 1997, leader=路飞), houseDTOs=[HouseDTO(house_pay=10000, address=新世界)])
4.4.3 逆映射
大家都发现了之前的案列,我们都是使用PO 对象转DTO对象,同时希望支持DTO对象转PO对象,MapStruct也支持PO 和DTO双向映射,主要通过@InheritInverseConfiguration注解标签实现。
/**
* 逆映射转换(在UserMapper接口里新增如下方法)
* @param personDTO
* @return
*/
@InheritInverseConfiguration(name = "covertNestedColl")
Person reverseCovertNestedColl(UserDTO user);
// 测试主类
public class MapStructStudyTest {
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy");
CompanyDTO companyDTO = CompanyDTO.builder().address("新世界").currentDate(format.parse("1997")).leader("路飞")
.numbers(4).name("草帽海贼团").build();
HouseDTO houseDTO = HouseDTO.builder().address("新世界").house_pay(10000).build();
UserDTO userDTO = UserDTO.builder().age("31").currentDay(format.parse("1997")).id(1L).describe("Java 开发")
.height("185").personName("路飞").source("10000")
.dto(companyDTO).houseDTOs(Lists.newArrayList(houseDTO)).build();
User user = UserMapper.INSTANCE.reverseCovertNestedColl(userDTO);
System.out.println(user);
}
}
//User(describe=Java 开发, id=1, name=null, age=31, source=10000, height=185.0, createTime=null, company=Company(name=草帽海贼团, address=新世界, numberOfPeople=null, createDate=null, createBy=null), houses=[House(address=新世界, price=null)])
注意:
@InheritInverseConfiguration表示方法应继承相应反向方法的反向配置。
建议指定name属性,对应逆映射方法名称。
可以看到上面执行效果部分字段为空,那是因为没有映射完全,在逆向映射时,嵌套的类也应该添加逆向方法,并且上面增加@InheritInverseConfiguration继承相应反向方法的反向配置。
4.4.4 继承映射
MapStruct 提供了很多方法级注解标签:Mapping,@BeanMapping,@IterableMapping 等等。有时候我们希望某些转换方法继承指定转换方法的MapStruct 注解标签配置,以节省@Mapper 接口到处都是相同的注解标签。我们可以通过@InheritConfiguration注解标签,实现我们希望的功能。通过声明@InheritConfiguration该方法,MapStruct可以搜索继承候选,以应用继承自该方法的注释。
/**
* 配置继承(在UserMapper接口里新增如下方法)
* @param user
* @param userDTO
*/
@InheritConfiguration(name="covertNestedColl")
void covertExtend(User user, @MappingTarget UserDTO userDTO);
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy");
Company company = Company.builder().address("新世界").createDate(format.parse("1997")).createBy("路飞")
.numberOfPeople(4).name("草帽海贼团").build();
House house = House.builder().address("新世界").price(10000).build();
User user = User.builder().age(31).createTime(format.parse("1997")).id("1").describe("Java 开发")
.height(185L).name("路飞").source(new BigDecimal(10000))
.company(company).houses(Arrays.asList(house)).build();
UserDTO userDTO = new UserDTO();
UserCovertBasic.INSTANCE.covertExtend(user, userDTO);
System.out.println(userDTO);
}
//UserDTO(describe=Java 开发, id=1, personName=null, age=31, source=10000, height=185.0, currentDay=null, dto=CompanyDTO(name=草帽海贼团, address=新世界, numbers=4, currentDate=Wed Jan 01 00:00:00 CST 1997, leader=路飞), houseDTOs=[HouseDTO(house_pay=10000, address=新世界)])
4.4.5 共享映射
有这样一种场景:我们经常在数据库中定义创建时间date_create、修改时间date_update 这样的字段,通常情况下几乎每个表都存在这样的字段,数据库里面的类型是java.sql.Timestamp类型,而我们一般都转为String类型便于显示。如果不用mapStruct的共享配置,那相当于在每个表对应的转化类里面配置 Timestamp到String的映射。但是如果有共享配置,我们只要配置一遍,然后在其他地方引入,达到共享的目的。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BasePO {
public String sysCreateDate;
public String sysCreateId;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BaseDTO {
public Date sysCreateDate;
public String sysCreateId;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Boss extends BasePO{
private String name;
private String phone;
private Date birthday;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BossDTO extends BaseDTO{
private String realName;
private String telephone;
private Date birthday;
@Override
public String toString() {
return "BossDTO{" +
"realName='" + realName + '\'' +
", telephone='" + telephone + '\'' +
", birthday=" + birthday +
", sysCreateDate=" + sysCreateDate +
", sysCreateId='" + sysCreateId + '\'' +
'}';
}
}
// 共享配置
@MapperConfig(unmappedTargetPolicy = ReportingPolicy.ERROR, uses = DateFormatUtil.class)
public interface CommonConfig {
BaseDTO convertBase(BasePO basePO);
}
public class DateFormatUtil {
public Date asDate(String date) {
try {
return date != null ? new SimpleDateFormat( "yyyy-MM-dd" )
.parse( date ) : null;
}
catch ( ParseException e ) {
throw new RuntimeException( e );
}
}
}
// 对象转换mapper
@Mapper(config = CommonConfig.class)
public interface BossMapper {
BossMapper INSTANCE = Mappers.getMapper(BossMapper.class);
@Mapping(source = "name", target="realName")
@Mapping(source = "phone", target="telephone")
BossDTO convertMapping(Boss boss);
}
// 主测试类
public class MapStructStudyTest {
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Boss boss = new Boss();
boss.setSysCreateDate("2022-10-21");
boss.setSysCreateId("1L");
boss.setName("胡歌");
boss.setBirthday(format.parse("1989-05-30"));
boss.setPhone("13855553333");
BossDTO bossDTO = BossMapper.INSTANCE.convertMapping(boss);
System.out.println(bossDTO);
}
}
注意:
MapStruct的共享映射通过注解@MapperConfig定义,然后在@Mapper的属性config中引入。
@MapperConfig注解具有与@Mapper注解相同的属性。
任何未通过@Mapper指定的属性都将从共享配置中继承,在@Mapper中指定的属性优先于通过引用的配置类指定的属性。
4.4.6 集合映射
集合属性映射转换包括基本集合属性转换和复杂属性转换。代码案例如下:
@Mapper(config = CommonConfig.class)
public interface BossMapper {
BossMapper INSTANCE = Mappers.getMapper(BossMapper.class);
@Mapping(source = "name", target="realName")
@Mapping(source = "phone", target="telephone")
BossDTO convertMapping(Boss boss);
/**
* 基本类型集合类型转换
* @param list
* @return
*/
List<Integer> convertCollBasic(List<String> list);
/**
* 复杂类型集合类型转换
* @param list
* @return
*/
List<BossDTO> convertCollObject(List<Boss> list);
}
public class MapStructStudyTest {
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Boss boss = new Boss();
boss.setSysCreateDate("2022-10-21");
boss.setSysCreateId("1L");
boss.setName("胡歌");
boss.setBirthday(format.parse("1989-05-30"));
boss.setPhone("13855553333");
Boss boss1 = new Boss();
boss1.setSysCreateDate("2022-10-21");
boss1.setSysCreateId("2L");
boss1.setName("彭于晏");
boss1.setBirthday(format.parse("1989-05-30"));
boss1.setPhone("13955554444");
List<Boss> bossList = new ArrayList<>();
bossList.add(boss);
bossList.add(boss1);
List<String> strList = Arrays.asList("1","2","3","4","5");
List<Integer> intList = BossMapper.INSTANCE.convertCollBasic(strList);
intList.forEach(System.out::println);
System.out.println();
System.out.println("=============上面是简单集合映射运行结果,下面是复杂映射运行结果===============");
System.out.println();
List<BossDTO> bossDTOS = BossMapper.INSTANCE.convertCollObject(bossList);
bossDTOS.forEach(System.out::println);
}
}
4.5 MapStruct使用注意踩坑点
赋值会出现空值。这是一个隐藏很深的坑,以至于运行后出现 Null 异常才发现,为什么会出现复制失败的情况呢,明明字段名称都是一样的,其实这跟我们使用了 lombok 有关,它和MapStruct的作用时机都是一样的,都是工作在编译阶段,可能会存在编译后历史代码。因此,出现一些奇奇怪怪的异常时,首先我们需要想到的是先用maven clean一下,清楚掉历史上编译留下的代码,这个操作往往非常有效。
五、总结
Java 对象映射工具种类丰富,开发者需要根据项目需求选择合适的工具。
0 条评论