前言
无论是常用的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();
复制代码
测试攻略
服务端硬件信息
- CPU信息:型号 Intel Core i5;主频 2.3 GHz;本人使用的是mac电脑,双核4线程
- 内存信息:大小 8G
- 硬盘信息:大小 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
他们之间的性能差距也跟实现原理有很大关系
- mapStruct会生成一个类,该类也是使用get、set的方式实现对象赋值
- BeanCopier是基于cglib动态代理做的对象转换
- spring BeanUtils、apache BeanUtils都是基于jdk动态代理