对象拷贝哪家强

前言

无论是常用的MVC三层架构还是DDD领域驱动模型中的四层架构,在平时的开发中,对象的转化是难以避免的,目前市面上的对象转换工具很多,使用什么工具比较合适呢?本文来测试看看

准备工作

@Entity
@Table(name = "account")
@Data
public class Account {

    @Id
    @Column(name = "id", updatable = false, nullable = false)
    protected Long id;

    @Column(name = "password")
    private String password;

    @Column(name = "mobile", length = 20)
    private String mobile;

    @Column(name = "mobile_area_code")
    private String mobileAreaCode;

    @Column(name = "email", length = 100)
    private String email;
}
复制代码
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AccountDto {

    protected Long id;

    private String password;

    private String mobile;

    private String mobileAreaCode;

    private String email;

}
复制代码

使用jpa查询数据库中的数据

public interface AccountRepository extends JpaRepository<Account, Long>, JpaSpecificationExecutor<Account> {
}
复制代码

在数据库表中插入1000条数据

CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email` varchar(100) DEFAULT NULL,
  `mobile_area_code` varchar(16) DEFAULT NULL,
  `mobile` varchar(100) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_email` (`email`),
  KEY `idx_key_part` (`mobile`,`mobile_area_code`),
  KEY `idx_password` (`password`)
) ENGINE=InnoDB AUTO_INCREMENT=11013 DEFAULT CHARSET=utf8mb4;


delimiter $$
CREATE PROCEDURE proc_initData1()
BEGIN
    declare i int;
    set i = 1;
    WHILE i<=1000 DO
       INSERT INTO account (`email`, `mobile_area_code`, `mobile`, `password`) VALUES (uuid(), '86', uuid(), uuid());
        SET i = i+1;
    END WHILE;
end $$
CALL proc_initData1();
复制代码

测试攻略

服务端硬件信息
  1. CPU信息:型号 Intel Core i5;主频 2.3 GHz;本人使用的是mac电脑,双核4线程
  2. 内存信息:大小 8G
  3. 硬盘信息:大小 256G
客户端硬件信息

同服务端,在同一个机器上

热身

Java为了实现 一次编译,处处运行的特性,将编译的过程分为两个部分,先编译成通用的中间形式–字节码,再由解释器逐条将字节码解释为机器码来执行。为了提高性能,JVM引入了即时编译器(JIT compiler),在程序运行时,解释器让代码可以直接运行,随着时间推移,即时编译器发挥作用,当发现某个方法或者代码块运行得特别频繁,这些代码会被认为是热点代码,直接编译成机器码,存储到内存中,下次运行可以直接读取内存。为了防止测试期间JIT机制的影响,开头的几次测试数据可以不计入结果中。

测试结果取平均

这个也是常见的测试方法,测试期间,可能会有JVM垃圾回收机制、机器其它进程、网络波动等等的影响,为了降低这些微小的差异影响最后的结果,我们一般是测试多次取平均

测试工具

apache ab压测工具

模式100个用户发起总共5000次请求

开始测试

为了偷懒,以下的测试我就不取平均了,会在多个请求中取一个较为平均的时间

以下测试得来的测试参数的信息可以看这个博客:www.jianshu.com/p/13b680f3e…

手动设置
@Slf4j
@RestController
@RequiredArgsConstructor
public class AccountController {

    private final AccountRepository accountRepository;


    @GetMapping("/api/v1/accounts")
    public List<AccountDto> getSecurityInfo() {
        List<Account> all = accountRepository.findAll();
        return all.stream().map(this::buildAccountDto).collect(Collectors.toList());
    }


    private AccountDto buildAccountDto(Account account) {
        return AccountDto.builder().id(account.getId())
                .mobile(account.getMobile())
                .mobileAreaCode(account.getMobileAreaCode())
                .email(account.getEmail())
                .password(account.getPassword())
                .build();
    }
}
复制代码

测试结果

it00013165@zhangxiaobindeMacBook-Pro  ~  ab -n 5000 -c 100 http://127.0.0.1:8080/api/v1/accounts
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 500 requests
Completed 1000 requests
Completed 1500 requests
Completed 2000 requests
Completed 2500 requests
Completed 3000 requests
Completed 3500 requests
Completed 4000 requests
Completed 4500 requests
Completed 5000 requests
Finished 5000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /api/v1/accounts
Document Length:        180001 bytes

Concurrency Level:      100
Time taken for tests:   21.273 seconds
Complete requests:      5000
Failed requests:        0
Total transferred:      900975000 bytes
HTML transferred:       900005000 bytes
Requests per second:    235.04 [#/sec] (mean)
Time per request:       425.460 [ms] (mean)
Time per request:       4.255 [ms] (mean, across all concurrent requests)
Transfer rate:          41360.31 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    4  13.0      1     223
Processing:    13  420 209.9    380    1616
Waiting:        1  342 214.1    305    1525
Total:         15  424 208.0    382    1617

Percentage of the requests served within a certain time (ms)
  50%    382
  66%    481
  75%    532
  80%    564
  90%    700
  95%    803
  98%    922
  99%   1060
 100%   1617 (longest request)
复制代码
apache BeanUtils
@Slf4j
@RestController
@RequiredArgsConstructor
public class AccountController {

    private final AccountRepository accountRepository;


    @GetMapping("/api/v1/accounts")
    public List<AccountDto> getSecurityInfo() {
        List<Account> all = accountRepository.findAll();
        return all.stream().map(a -> {
            AccountDto accountDto = new AccountDto();
            try {
                BeanUtils.copyProperties(accountDto, a);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
            return accountDto;
        }).collect(Collectors.toList());
    }
}
复制代码

测试结果

it00013165@zhangxiaobindeMacBook-Pro  ~  ab -n 5000 -c 100 http://127.0.0.1:8080/api/v1/accounts
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 500 requests
Completed 1000 requests
Completed 1500 requests
Completed 2000 requests
Completed 2500 requests
Completed 3000 requests
Completed 3500 requests
Completed 4000 requests
Completed 4500 requests
Completed 5000 requests
Finished 5000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /api/v1/accounts
Document Length:        180001 bytes

Concurrency Level:      100
Time taken for tests:   90.601 seconds
Complete requests:      5000
Failed requests:        0
Total transferred:      900975000 bytes
HTML transferred:       900005000 bytes
Requests per second:    55.19 [#/sec] (mean)
Time per request:       1812.028 [ms] (mean)
Time per request:       18.120 [ms] (mean, across all concurrent requests)
Transfer rate:          9711.31 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   1.5      0      43
Processing:    34 1799 406.2   1767    4064
Waiting:       30 1780 409.8   1746    4060
Total:         35 1800 406.2   1767    4064

Percentage of the requests served within a certain time (ms)
  50%   1767
  66%   1935
  75%   2037
  80%   2105
  90%   2306
  95%   2496
  98%   2768
  99%   2995
 100%   4064 (longest request)
复制代码
spring BeanUtils
@Slf4j
@RestController
@RequiredArgsConstructor
public class AccountController {

    private final AccountRepository accountRepository;


    @GetMapping("/api/v1/accounts")
    public List<AccountDto> getSecurityInfo() {
        List<Account> all = accountRepository.findAll();
        return all.stream().map(a -> {
            AccountDto accountDto = new AccountDto();
            BeanUtils.copyProperties(a, accountDto);
            return accountDto;
        }).collect(Collectors.toList());
    }
}
复制代码

测试结果

 it00013165@zhangxiaobindeMacBook-Pro  ~  ab -n 5000 -c 100 http://127.0.0.1:8080/api/v1/accounts
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 500 requests
Completed 1000 requests
Completed 1500 requests
Completed 2000 requests
Completed 2500 requests
Completed 3000 requests
Completed 3500 requests
Completed 4000 requests
Completed 4500 requests
Completed 5000 requests
Finished 5000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /api/v1/accounts
Document Length:        180001 bytes

Concurrency Level:      100
Time taken for tests:   64.360 seconds
Complete requests:      5000
Failed requests:        0
Total transferred:      900975000 bytes
HTML transferred:       900005000 bytes
Requests per second:    77.69 [#/sec] (mean)
Time per request:       1287.207 [ms] (mean)
Time per request:       12.872 [ms] (mean, across all concurrent requests)
Transfer rate:          13670.82 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   3.1      0      69
Processing:    30 1279 506.9   1215    5291
Waiting:       25 1172 496.2   1150    5289
Total:         31 1280 506.9   1215    5291

Percentage of the requests served within a certain time (ms)
  50%   1215
  66%   1341
  75%   1469
  80%   1570
  90%   1896
  95%   2200
  98%   2658
  99%   2974
 100%   5291 (longest request)
复制代码
mapStruct
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.4.2.Final</version>
</dependency>
复制代码
@Mapper
public interface AccountMapper {

    AccountMapper INSTANCE = Mappers.getMapper( AccountMapper.class );

    AccountDto accountToAccountDto(Account account);
}
复制代码
@Slf4j
@RestController
@RequiredArgsConstructor
public class AccountController {

    private final AccountRepository accountRepository;


    @GetMapping("/api/v1/accounts")
    public List<AccountDto> getSecurityInfo() {
        List<Account> all = accountRepository.findAll();
        return all.stream().map(AccountMapper.INSTANCE::accountToAccountDto).collect(Collectors.toList());
    }
}
复制代码

测试结果

it00013165@zhangxiaobindeMacBook-Pro  ~  ab -n 5000 -c 100 http://127.0.0.1:8080/api/v1/accounts
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 500 requests
Completed 1000 requests
Completed 1500 requests
Completed 2000 requests
Completed 2500 requests
Completed 3000 requests
Completed 3500 requests
Completed 4000 requests
Completed 4500 requests
Completed 5000 requests
Finished 5000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /api/v1/accounts
Document Length:        180001 bytes

Concurrency Level:      100
Time taken for tests:   20.061 seconds
Complete requests:      5000
Failed requests:        0
Total transferred:      900975000 bytes
HTML transferred:       900005000 bytes
Requests per second:    249.24 [#/sec] (mean)
Time per request:       401.213 [ms] (mean)
Time per request:       4.012 [ms] (mean, across all concurrent requests)
Transfer rate:          43859.89 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    3  12.5      0     141
Processing:    25  395 195.4    341    2301
Waiting:        8  323 199.1    291    2280
Total:         25  398 194.3    344    2302

Percentage of the requests served within a certain time (ms)
  50%    344
  66%    436
  75%    489
  80%    527
  90%    646
  95%    759
  98%    922
  99%   1042
 100%   2302 (longest request)
复制代码
原理

项目编译完成后,我们点开target文件夹,就会发现文件夹中生成了AccountMapper接口的实现类,实现类中是使用get、set的方式对对象进行赋值的

public class AccountMapperImpl implements AccountMapper {
    public AccountMapperImpl() {
    }

    public AccountDto accountToAccountDto(Account account) {
        if (account == null) {
            return null;
        } else {
            AccountDtoBuilder accountDto = AccountDto.builder();
            accountDto.id(account.getId());
            accountDto.password(account.getPassword());
            accountDto.mobile(account.getMobile());
            accountDto.mobileAreaCode(account.getMobileAreaCode());
            accountDto.email(account.getEmail());
            return accountDto.build();
        }
    }
}
复制代码
BeanCopier

beanCopier是可以自定义转换器的

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>3.3.0</version>
</dependency>
复制代码
@Slf4j
@RestController
@RequiredArgsConstructor
public class AccountController {

    private final AccountRepository accountRepository;


    @GetMapping("/api/v1/accounts")
    public List<AccountDto> getSecurityInfo() {
        List<Account> all = accountRepository.findAll();
        return all.stream().map(a -> {
          	// 不使用自定义转换器
            BeanCopier beanCopier = BeanCopier.create(Account.class, AccountDto.class, false);
            AccountDto accountDto = new AccountDto();
            beanCopier.copy(a, accountDto, null);
            return accountDto;
        }).collect(Collectors.toList());
    }
}
复制代码
it00013165@zhangxiaobindeMacBook-Pro  ~  ab -n 5000 -c 100 http://127.0.0.1:8080/api/v1/accounts
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 500 requests
Completed 1000 requests
Completed 1500 requests
Completed 2000 requests
Completed 2500 requests
Completed 3000 requests
Completed 3500 requests
Completed 4000 requests
Completed 4500 requests
Completed 5000 requests
Finished 5000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /api/v1/accounts
Document Length:        180001 bytes

Concurrency Level:      100
Time taken for tests:   26.136 seconds
Complete requests:      5000
Failed requests:        0
Total transferred:      900975000 bytes
HTML transferred:       900005000 bytes
Requests per second:    191.30 [#/sec] (mean)
Time per request:       522.729 [ms] (mean)
Time per request:       5.227 [ms] (mean, across all concurrent requests)
Transfer rate:          33664.01 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    3  13.5      0     264
Processing:    18  515 267.9    433    2665
Waiting:       12  363 268.5    264    2307
Total:         18  518 266.7    437    2665

Percentage of the requests served within a certain time (ms)
  50%    437
  66%    535
  75%    609
  80%    674
  90%    849
  95%   1048
  98%   1334
  99%   1506
 100%   2665 (longest request)
复制代码

总结

经过了多次压测之后,性能排名如下

手动设置≈mapStruct>BeanCopier>spring BeanUtils>apache BeanUtils

他们之间的性能差距也跟实现原理有很大关系

  1. mapStruct会生成一个类,该类也是使用get、set的方式实现对象赋值
  2. BeanCopier是基于cglib动态代理做的对象转换
  3. spring BeanUtils、apache BeanUtils都是基于jdk动态代理
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享