使用MapStruct 解决对象之间转换、深拷贝问题

在日常开发中,我们会定义多种不同的Javabean,比如DTO(Data Transfer Object:数据传输对象),DO(Data Object:数据库映射对象,与数据库一一映射),VO(View Object:显示层对象,通常是 Web 向模板渲染引擎层传输的对象)等等这些对象。在这些对象与对象之间转换通常是调对象的set和get方法进行复制,这种转换通常也是很无聊的操作,如果有一个专门的工具来解决Javabean之间的转换问题,让我们从这种无聊的转换操作中解放出来。

首先梳理出来现在有哪些对象拷贝的方式:

  • 浅拷贝
    • Apache的BeanUtils基于反射实现,BeanUtils是Apache commens组件里面的成员,由Apache提供的一套开源api,用于简化对javaBean的操作,能够对基本类型自动转换。
    • Spring的BeanUtils基于反射实现,BeanUtils是Spring框架提供的对Java反射和自省API的包装。其主要目的是利用反射机制对JavaBean的属性进行处理。
    • BeanCopier动态代理实现,BeanCopier是Cglib包中的一个类,用于对象的复制。目标对象必须先实例化 而且对象必须要有setter方法。由于BeanCopier是动态代理实现所以性能上比前两个要好的多
  • 深拷贝
    • Mapstruct:MapStruct是一个Java注释处理器,用于生成类型安全的bean映射类。MapStruct具有以下优点:
      • 速度快:使用普通的方法代替反射
      • 编译时类型安全性 : 只能映射彼此的对象和属性,不会将商品实体意外映射到用户DTO等
数据量ApacheSpringMapStructBeanCopier
100w391ms250ms45ms57ms
10w82ms34ms8ms10ms
1w30ms19ms2ms7ms
1k15ms6ms1ms5ms
1005ms3ms1ms4ms
102ms1ms1ms4ms

根据测试结果,我们可以得出在速度方面,MapStruct是最好的,执行速度是 Apache BeanUtils 的10倍、Spring BeanUtils 的 4-5倍、和 BeanCopier 的速度差不多。
由此可以看出,在大数据量级的情况下,MapStruct 和 BeanCopier 都有着较高的性能优势,其中 MapStruct 尤为优秀。如果你仅是在日常处理少量的对象时,选取哪个其实变得并不重要,但数据量大时建议还是使用MapStruct 或 BeanCopier 的方式,提高接口性能。

 

实战示例

maven依赖引入

方式1

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.3.0.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.3.0.Final</version>
    <scope>provided</scope>
</dependency>

方式2

 <dependency>
     <groupId>org.mapstruct</groupId>
     <artifactId>mapstruct</artifactId>
     <version>1.4.2.Final</version>
</dependency>

<build>
	<!-- 配置lombok 和mapStruct注解处理器 -->
    <pluginManagement>
        <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.4.2.Final</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.18.12</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

实体对象

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {
    // 账号
    private String account;
    // 金额
    private Integer amt;
}
@Data
@AllArgsConstructor
public class User {
    private String userName;
    private List<String> addresses;
}
@Data
public class CreateAccountDTO {
    private String key;
    private List<Account> accounts;
}
@Data
public class CreateAccountVO {
    private String visitKey;
    private User user;
    private List<Account> accounts;
}

转换

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

@Mapper
public interface AccountConvertUtils {
    AccountConvertUtils INSTANCE = Mappers.getMapper(AccountConvertUtils.class);

    // 普通的映射
    CreateAccountVO convert(CreateAccountDTO createAccountDTO);

    // 类型转换的映射
    @Mappings({
            @Mapping(target = "visitKey", source = "key"),
    })
    CreateAccountVO convertWithMapping(CreateAccountDTO createAccountDTO);

    // 多对一映射
    @Mappings({
            @Mapping(target = "visitKey", source = "createAccountDTO.key"),
    })
    CreateAccountVO convert(User user, CreateAccountDTO createAccountDTO);
}

测试用例

public class DemoApp {
    
    public static void main(String[] args) {
        CreateAccountDTO createAccountDTO = new CreateAccountDTO();
        List<Account> accounts = new ArrayList<>();
        accounts.add(new Account("ACT2021051200000001", 1000));
        accounts.add(new Account("ACT2021051200000002", 5000));
        createAccountDTO.setKey("326D1478E3914C8A");
        createAccountDTO.setAccounts(accounts);

        User user = new User("Jaemon", Lists.newArrayList("SZ", "NS"));
		
        // 执行1
//        CreateAccountVO createAccountVO = AccountConvertUtils.INSTANCE.convert(createAccountDTO);
        // 执行2
//        CreateAccountVO createAccountVO = AccountConvertUtils.INSTANCE.convertWithMapping(createAccountDTO);
        // 执行3
        CreateAccountVO createAccountVO = AccountConvertUtils.INSTANCE.convert(user, createAccountDTO);

        createAccountDTO.getAccounts().add(new Account("ACT2021051200000003", 1010));

        System.out.println(createAccountDTO.toString());
        System.out.println(createAccountVO.toString());
    }
}

运行输出

// 执行1打印
CreateAccountDTO(key=326D1478E3914C8A, accounts=[Account(account=ACT2021051200000001, amt=1000), Account(account=ACT2021051200000002, amt=5000), Account(account=ACT2021051200000003, amt=1010)])
CreateAccountVO(visitKey=null, user=null, accounts=[Account(account=ACT2021051200000001, amt=1000), Account(account=ACT2021051200000002, amt=5000)])
    

// 执行2打印    
CreateAccountDTO(key=326D1478E3914C8A, accounts=[Account(account=ACT2021051200000001, amt=1000), Account(account=ACT2021051200000002, amt=5000), Account(account=ACT2021051200000003, amt=1010)])
CreateAccountVO(visitKey=326D1478E3914C8A, user=null, accounts=[Account(account=ACT2021051200000001, amt=1000), Account(account=ACT2021051200000002, amt=5000)])
    

// 执行3打印    
CreateAccountDTO(key=326D1478E3914C8A, accounts=[Account(account=ACT2021051200000001, amt=1000), Account(account=ACT2021051200000002, amt=5000), Account(account=ACT2021051200000003, amt=1010)])
CreateAccountVO(visitKey=326D1478E3914C8A, user=User(userName=Jaemon, addresses=[SZ, NS]), accounts=[Account(account=ACT2021051200000001, amt=1000), Account(account=ACT2021051200000002, amt=5000)])    

 

References

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jaemon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值