目录
前言
源码地址
代码示例
引入依赖
先定两个实体用于转换
定义一个接口让所有转换器都集成
Apache BeanUtils
BeanCopier
bean-mapping
bean-mapping-asm
Dozer
自己写get/set
JMapper
json2json
MapStruct(推荐)
ModelMapper
OriKa
Spring BeanUtils
测试代码
测试结果绘制成图
总结
前言
本文章分别测试的对象转换工具为:
MapStruct、JMapper、ModelMapper、Dozer、OriKa、BeanCopier、自己写get/set、
json2json、Apache BeanUtils、Spring BeanUtils、bean-mapping、bean-mapping-asm
源码地址
lasse-vo2dto: 测试市面上常用的实体转换工具的性能
代码示例
引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Apache BeanUtils Begin -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<!-- Apache BeanUtils End -->
<!-- https://mvnrepository.com/artifact/com.github.houbb/bean-mapping-core -->
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>bean-mapping-core</artifactId>
<version>0.2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.houbb/bean-mapping-asm -->
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>bean-mapping-asm</artifactId>
<version>0.2.6</version>
</dependency>
<!-- MapStruct begin -->
<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</dependency>
<!-- MapStruct end -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.4.6</version>
</dependency>
<!--Dozer -->
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer-spring</artifactId>
<version>5.5.1</version>
</dependency>
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.5.1</version>
</dependency>
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.googlecode.jmapper-framework</groupId>
<artifactId>jmapper-core</artifactId>
<version>1.6.1.CR2</version>
</dependency>
<!-- http://modelmapper.org/getting-started/-->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
先定两个实体用于转换
public class UserVO {
/**
* 自增ID
*/
private Long id;
/**
* 用户ID
*/
private String userId;
/**
* 用户昵称
*/
private String userNickName;
/**
* 注册时间
*/
private Date createTime;
//get/set/toString等方法省略。。。
}
/**
* 用户DTO
*/
public class UserDTO {
/**
* 用户ID
*/
private String userId;
/**
* 用户昵称
*/
private String userNickName;
/**
* 注册时间
*/
private Date createTime;
//get/set/toString等方法省略。。。
}
定义一个接口让所有转换器都集成
/**
* 对象装配器接口
*/
public interface IAssembler<SOURCE, TARGET> {
TARGET sourceToTarget(SOURCE var);
}
Apache BeanUtils
@Component
public class ApacheCopyPropertiesAssembler implements IAssembler<UserVO, UserDTO> {
@Override
public UserDTO sourceToTarget(UserVO var) {
UserDTO userDTO = new UserDTO();
try {
BeanUtils.copyProperties(userDTO, var);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return userDTO;
}
}
BeanCopier
@Component
public class BeanCopierAssembler implements IAssembler<UserVO, UserDTO> {
@Override
public UserDTO sourceToTarget(UserVO var) {
UserDTO userDTO = new UserDTO();
BeanCopier beanCopier = BeanCopier.create(var.getClass(), userDTO.getClass(), false);
beanCopier.copy(var, userDTO, null);
return userDTO;
}
}
bean-mapping
@Component
public class BeanMappingAssembler implements IAssembler<UserVO, UserDTO> {
@Override
public UserDTO sourceToTarget(UserVO var) {
UserDTO userDTO = new UserDTO();
BeanUtil.copyProperties(var, userDTO);
return userDTO;
}
}
bean-mapping-asm
@Component
public class BeanMappingASMAssembler implements IAssembler<UserVO, UserDTO> {
@Override
public UserDTO sourceToTarget(UserVO var) {
UserDTO userDTO = new UserDTO();
AsmBeanUtil.copyProperties(var, userDTO);
return userDTO;
}
}
Dozer
@Component
public class DozerAssembler implements IAssembler<UserVO, UserDTO> {
private static DozerBeanMapper mapper = new DozerBeanMapper();
@Override
public UserDTO sourceToTarget(UserVO var) {
return mapper.map(var, UserDTO.class);
}
}
自己写get/set
@Component("getSetAssembler")
public class GetSetAssembler implements IAssembler<UserVO, UserDTO> {
@Override
public UserDTO sourceToTarget(UserVO var) {
UserDTO userDTO = new UserDTO();
userDTO.setUserId(var.getUserId());
userDTO.setUserNickName(var.getUserNickName());
userDTO.setCreateTime(var.getCreateTime());
return userDTO;
}
}
JMapper
@Component("jMapperAssembler")
public class JMapperAssembler implements IAssembler<UserVO, UserDTO> {
@Override
public UserDTO sourceToTarget(UserVO var) {
JMapper<UserDTO, UserVO> jMapper = new JMapper<>(UserDTO.class, UserVO.class, new JMapperAPI()
.add(JMapperAPI.mappedClass(UserDTO.class)
.add(JMapperAPI.attribute("userId")
.value("userId"))
.add(JMapperAPI.attribute("userNickName")
.value("userNickName"))
.add(JMapperAPI.attribute("createTime")
.value("createTime"))
));
return jMapper.getDestination(var);
}
}
json2json
@Component
public class Json2JsonAssembler implements IAssembler<UserVO, UserDTO> {
@Override
public UserDTO sourceToTarget(UserVO var) {
String strJson = JSON.toJSONString(var);
return JSON.parseObject(strJson, UserDTO.class);
}
}
MapStruct(推荐)
@MapperConfig
public interface IMapping<SOURCE, TARGET> {
/**
* 映射同名属性
*/
@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
TARGET sourceToTarget(SOURCE var1);
/**
* 反向,映射同名属性
*/
@InheritInverseConfiguration(name = "sourceToTarget")
SOURCE targetToSource(TARGET var1);
/**
* 映射同名属性,集合形式
*/
@InheritConfiguration(name = "sourceToTarget")
List<TARGET> sourceToTarget(List<SOURCE> var1);
/**
* 反向,映射同名属性,集合形式
*/
@InheritConfiguration(name = "targetToSource")
List<SOURCE> targetToSource(List<TARGET> var1);
/**
* 映射同名属性,集合流形式
*/
List<TARGET> sourceToTarget(Stream<SOURCE> stream);
/**
* 反向,映射同名属性,集合流形式
*/
List<SOURCE> targetToSource(Stream<TARGET> stream);
}
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, unmappedSourcePolicy = ReportingPolicy.IGNORE)
public interface UserDTOMapping extends IMapping<UserVO, UserDTO> {
/** 用于测试的单例 */
IMapping<UserVO, UserDTO> INSTANCE = Mappers.getMapper(UserDTOMapping.class);
@Mapping(target = "userId", source = "userId")
@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Override
UserDTO sourceToTarget(UserVO var1);
@Mapping(target = "userId", source = "userId")
@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Override
UserVO targetToSource(UserDTO var1);
}
@Component
public class MapStructAssembler implements IAssembler<UserVO, UserDTO> {
@Resource
private IMapping<UserVO, UserDTO> userDTOMapping;
@Override
public UserDTO sourceToTarget(UserVO var) {
return userDTOMapping.sourceToTarget(var);
}
}
ModelMapper
@Component
public class ModelMapperAssembler implements IAssembler<UserVO, UserDTO> {
private static ModelMapper modelMapper = new ModelMapper();
static {
modelMapper.addMappings(new PropertyMap<UserVO, UserDTO>() {
@Override
protected void configure() {
// 属性值不一样可以自己操作
map().setUserId(source.getUserId());
}
});
}
@Override
public UserDTO sourceToTarget(UserVO var) {
return modelMapper.map(var, UserDTO.class);
}
}
OriKa
@Component
public class OrikaAssembler implements IAssembler<UserVO, UserDTO> {
/**
* 构造一个MapperFactory
*/
private static MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
static {
mapperFactory.classMap(UserDTO.class, UserVO.class)
.field("userId", "userId") // 字段不一致时可以指定
.byDefault()
.register();
}
@Override
public UserDTO sourceToTarget(UserVO var) {
return mapperFactory.getMapperFacade().map(var, UserDTO.class);
}
}
Spring BeanUtils
@Component
public class SpringCopyPropertiesAssembler implements IAssembler<UserVO, UserDTO> {
@Override
public UserDTO sourceToTarget(UserVO var) {
UserDTO userDTO = new UserDTO();
BeanUtils.copyProperties(var, userDTO);
return userDTO;
}
}
测试代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApiTest {
private Logger logger = LoggerFactory.getLogger(ApiTest.class);
@Resource(name = "apacheCopyPropertiesAssembler")
private IAssembler<UserVO, UserDTO> apacheCopyPropertiesAssembler;
@Resource(name = "beanCopierAssembler")
private IAssembler<UserVO, UserDTO> beanCopierAssembler;
@Resource(name = "beanMappingAssembler")
private IAssembler<UserVO, UserDTO> beanMappingAssembler;
@Resource(name = "beanMappingASMAssembler")
private IAssembler<UserVO, UserDTO> beanMappingASMAssembler;
@Resource(name = "getSetAssembler")
private IAssembler<UserVO, UserDTO> getSetAssembler;
@Resource(name = "mapStructAssembler")
private IAssembler<UserVO, UserDTO> mapStructAssembler;
@Resource(name = "springCopyPropertiesAssembler")
private IAssembler<UserVO, UserDTO> springCopyPropertiesAssembler;
@Resource(name = "orikaAssembler")
private IAssembler<UserVO, UserDTO> orikaAssembler;
@Resource(name = "dozerAssembler")
private IAssembler<UserVO, UserDTO> dozerAssembler;
@Resource(name = "modelMapperAssembler")
private IAssembler<UserVO, UserDTO> modelMapperAssembler;
@Resource(name = "jMapperAssembler")
private IAssembler<UserVO, UserDTO> jMapperAssembler;
@Resource(name = "json2JsonAssembler")
private IAssembler<UserVO, UserDTO> json2JsonAssembler;
private Long cycleIndex=100000L;
private UserVO userVO = new UserVO();
@Test
public void all(){
System.out.println("各跑"+cycleIndex+"次");
test_apacheCopyPropertiesAssembler();
test_beanCopierAssembler();
test_beanMappingAssembler();
test_beanMappingASMAssembler();
test_getSetAssembler();
test_mapStructAssembler();
test_springCopyPropertiesAssembler();
test_orikaAssembler();
test_dozerAssembler();
test_modelMapperAssembler();
test_jMapperAssembler();
test_json2JsonAssembler();
}
@Before
public void initData() {
userVO.setId(1001L);
userVO.setUserId("007");
userVO.setUserNickName("lasse");
userVO.setCreateTime(new Date());
}
@Test
public void test_apacheCopyPropertiesAssembler() {
long start = System.currentTimeMillis();
for (int i = 0; i < cycleIndex; i++) {
UserDTO userDTO = apacheCopyPropertiesAssembler.sourceToTarget(userVO);
}
System.out.println("apacheCopyPropertiesAssembler方法耗时:" + (System.currentTimeMillis() - start) + "ms");
// 方法耗时:2050ms
}
@Test
public void test_beanCopierAssembler() {
long start = System.currentTimeMillis();
for (int i = 0; i < cycleIndex; i++) {
UserDTO userDTO = beanCopierAssembler.sourceToTarget(userVO);
}
System.out.println("beanCopierAssembler方法耗时:" + (System.currentTimeMillis() - start) + "ms");
// 方法耗时:53ms
}
@Test
public void test_beanMappingAssembler() {
long start = System.currentTimeMillis();
for (int i = 0; i < cycleIndex; i++) {
UserDTO userDTO = beanMappingAssembler.sourceToTarget(userVO);
}
System.out.println("beanMappingAssembler方法耗时:" + (System.currentTimeMillis() - start) + "ms");
// 方法耗时:162ms
}
@Test
public void test_beanMappingASMAssembler() {
long start = System.currentTimeMillis();
for (int i = 0; i < cycleIndex; i++) {
UserDTO userDTO = beanMappingASMAssembler.sourceToTarget(userVO);
}
System.out.println("beanMappingASMAssembler方法耗时:" + (System.currentTimeMillis() - start) + "ms");
// 方法耗时:90ms
}
// 方法耗时:3ms
@Test
public void test_getSetAssembler() {
long start = System.currentTimeMillis();
for (int i = 0; i < cycleIndex; i++) {
UserDTO userDTO = getSetAssembler.sourceToTarget(userVO);
}
System.out.println("getSetAssembler方法耗时:" + (System.currentTimeMillis() - start) + "ms");
}
@Test
public void test_mapStructAssembler() {
long start = System.currentTimeMillis();
for (int i = 0; i < cycleIndex; i++) {
UserDTO userDTO = mapStructAssembler.sourceToTarget(userVO);
}
System.out.println("mapStructAssembler方法耗时:" + (System.currentTimeMillis() - start) + "ms");
// 方法耗时:4ms
}
@Test
public void test_springCopyPropertiesAssembler() {
long start = System.currentTimeMillis();
for (int i = 0; i < cycleIndex; i++) {
UserDTO userDTO = springCopyPropertiesAssembler.sourceToTarget(userVO);
}
System.out.println("springCopyPropertiesAssembler方法耗时:" + (System.currentTimeMillis() - start) + "ms");
// 方法耗时:64ms
}
@Test
public void test_orikaAssembler() {
long start = System.currentTimeMillis();
for (int i = 0; i < cycleIndex; i++) {
UserDTO userDTO = orikaAssembler.sourceToTarget(userVO);
}
System.out.println("orikaAssembler方法耗时:" + (System.currentTimeMillis() - start) + "ms");
}
@Test
public void test_dozerAssembler() {
long start = System.currentTimeMillis();
for (int i = 0; i < cycleIndex; i++) {
UserDTO userDTO = dozerAssembler.sourceToTarget(userVO);
}
System.out.println("dozerAssembler方法耗时:" + (System.currentTimeMillis() - start) + "ms");
}
@Test
public void test_modelMapperAssembler() {
long start = System.currentTimeMillis();
for (int i = 0; i < cycleIndex; i++) {
UserDTO userDTO = modelMapperAssembler.sourceToTarget(userVO);
}
System.out.println("modelMapperAssembler方法耗时:" + (System.currentTimeMillis() - start) + "ms");
}
@Test
public void test_jMapperAssembler() {
long start = System.currentTimeMillis();
for (int i = 0; i <cycleIndex; i++) {
UserDTO userDTO = jMapperAssembler.sourceToTarget(userVO);
}
System.out.println("jMapperAssembler方法耗时:" + (System.currentTimeMillis() - start) + "ms");
}
@Test
public void test_json2JsonAssembler() {
long start = System.currentTimeMillis();
for (int i = 0; i < cycleIndex; i++) {
UserDTO userDTO = json2JsonAssembler.sourceToTarget(userVO);
}
System.out.println("json2JsonAssembler方法耗时:" + (System.currentTimeMillis() - start) + "ms");
}
}
测试结果绘制成图
用于对象属性转换有12种,接下来我们分别测试这12种属性转换操作分别在一百次、一千次、一万次、十万次、一百万次时候的性能时间对比。
总结
BeanUtils.copyProperties
是大家代码里最常出现的工具类,但只要你不把它用错成Apache
包下的,而是使用 Spring 提供的,就基本还不会对性能造成多大影响。- 但如果说性能更好,可替代手动
get、set
的,还是MapStruct
更好用,因为它本身就是在编译期生成get、set
代码,和我们写get、set
一样。 - 其他一些组件包主要基于
AOP
、ASM
、CGlib
,的技术手段实现的,所以也会有相应的性能损耗