美文网首页
JAVA 对象转化

JAVA 对象转化

作者: 不怕天黑_0819 | 来源:发表于2022-03-07 16:07 被阅读0次

前言

在我们的实际编码工作中会碰到很多对象转化的场景,尤其像DO、DTO、VO等对象的转化非常多,通常我们会选择第三方工具或者自己手写,但他们或多或少都有一些自己的缺点。下面详细说说常用的方案和最佳方案,还有各种方案之间的耗时数据比较。

常用方案

手写

public class TeacherConvertor implements ConvertorI<TeacherEntity, TeacherDO> {

    /**
     * 对象转化
     *
     * @param req
     * @return
     */
    public static TeacherEntity dataToEntity(TeacherDO req){
        if (req == null) {
            return null;
        }
        TeacherEntity result = new TeacherEntity();
        result.setAddress(req.getAddress());
        result.setAge(req.getAge());
        result.setName(req.getName());
        result.setSex(req.getSex());
        return result;
    }

    /**
     * 对象转化
     *
     * @param req
     * @return
     */
    public static TeacherDO entityToData(TeacherEntity req) {
        if (req == null) {
            return null;
        }
        TeacherDO result = new TeacherDO();
        result.setAddress(req.getAddress());
        result.setAge(req.getAge());
        result.setName(req.getName());
        result.setSex(req.getSex());
        return result;
    }
}

这种方案的缺点是需要手写很多繁琐的代码,一不小心眼花了还容易设置错了字段

apache.BeanUtils

org.apache.commons.beanutils.BeanUtils.copyProperties(do, entity);

这种方案因为用到反射的原因,同时本身设计问题,性能比较差,其次针对复杂场景支持能力不足

spring.BeanUtils

org.springframework.beans.BeanUtils.copyProperties(do, entity);

这种方案针对apache的BeanUtils做了很多优化,整体性能提升不少,不过还是比不上原生代码处理,其次针对复杂场景支持能力不足

PropertyUtils

PropertyUtils.copyProperties(do, entity);

这种方案因为用到反射的原因,性能比手写要差,其次针对复杂场景支持能力不足

beanCopier

BeanCopier copier = BeanCopier.create(TeacherDO.class, TeacherEntity.class, false);
copier.copy(do, entity, null);

这种方案动态生成一个要代理类的子类,其实就是通过字节码方式转换成性能最好的get和set方式,重要的开销在创建BeanCopier,整体性能接近原生代码处理,但是针对复杂场景支持能力不足

fastjson

TeacherEntity entity = JSON.parseObject(JSON.toJSONString(teacherDO), TeacherEntity.class);

这种方案因为通过生成中间json格式字符串,然后再转化成目标对象,性能比较差,同时因为中间会生成json格式字符串,如果转化过多,gc会非常频繁,同时针对复杂场景支持能力不足

最佳方案

mapstruct

mapstruct方案的性能和原生代码一样,而且代码量还少,另外相对手写方案来说不容易出错,同时支持各种复杂转化场景

使用方式

如上手写方案换成mapstruct的话

convertor声明

@Mapper
public interface TeacherMapper {

    TeacherMapper MAPPER = Mappers.getMapper( TeacherMapper.class );

    TeacherEntity dataToEntity(TeacherDO req);

    TeacherDO entityToData(TeacherEntity req);
}

convertor使用

TeacherEntity entity = TeacherMapper.MAPPER.dataToEntity(do);

原理分析

mapstruct的原理和lombok差不多,都是通过编译时注解处理器来实现的。在编译的时候识别注解,然后生成一些类和代码。

如上我们申明了一个TeacherMapper。在编译之后会多生成一个TeacherMapperImpl类
里面的代码和我们手写的TeacherConvertor代码基本一样
在使用中用到的TeacherMapper.MAPPER其实就是这个TeacherMapperImpl类。

更多支持场景

  • 字段不同名转化
@Mapper
public interface TeacherMapper {

    TeacherMapper MAPPER = Mappers.getMapper( TeacherMapper.class );

    @Mapping(source = "address", target = "addressInfo")
    TeacherEntity dataToEntity(TeacherDO req);

    TeacherDO entityToData(TeacherEntity req);
}

  • 简单对象转化
  • 集合类型转化
  • 对象嵌套转化
  • N个对象转一个对象
  • 转化能力自定义扩展
  • 还有更多功能可以去github上了解下

性能比较

测试代码

public class MainTest {

    /**
     * 转化对象
     */
    private TeacherDO teacherDO;

    /**
     * 转化次数
     */
    private final static int count = 1000000;

    @Before
    public void before() {
        teacherDO = new TeacherDO("name", "sex", "address", "age");
    }

    @Test
    public void userCopy() {
        long startTime = System.currentTimeMillis();
        for (int i = 1; i <= count; i++) {
            // log.debug(i+"");
            TeacherEntity entity = TeacherConvertor.dataToEntity(teacherDO);
        }
        System.out.println("userCopy time" + (System.currentTimeMillis() - startTime));
    }

    @Test
    public void mapstruct() {
        long startTime = System.currentTimeMillis();
        for (int i = 1; i <=count; i++) {
            // log.debug(i+"");
            TeacherEntity entity = TeacherMapper.MAPPER.dataToEntity(teacherDO);
        }
        System.out.println("mapstruct time" + (System.currentTimeMillis() - startTime));
    }

    @Test
    public void beanCopier() {
        BeanCopier copier = BeanCopier.create(TeacherDO.class, TeacherEntity.class, false);
        long startTime = System.currentTimeMillis();
        for (int i = 1; i <= count; i++) {
            // log.debug(i+"");
            TeacherEntity entity = new TeacherEntity();
            copier.copy(teacherDO, entity, null);
        }
        System.out.println("beanCopier time" + (System.currentTimeMillis() - startTime));
    }

    @Test
    public void springBeanUtils(){
        long startTime = System.currentTimeMillis();
        for (int i = 1; i <=count; i++) {
            // log.debug(i+"");
            TeacherEntity entity = new TeacherEntity();
            org.springframework.beans.BeanUtils.copyProperties(teacherDO, entity);
        }
        System.out.println("springBeanUtils time" + (System.currentTimeMillis() - startTime));
    }

    @Test
    public void apacheBeanUtils() throws InvocationTargetException, IllegalAccessException {
        long startTime = System.currentTimeMillis();
        for (int i = 1; i <=count; i++) {
            // log.debug(i+"");
            TeacherEntity entity = new TeacherEntity();
            org.apache.commons.beanutils.BeanUtils.copyProperties(teacherDO, entity);
        }
        System.out.println("apacheBeanUtils time" + (System.currentTimeMillis() - startTime));
    }

    @Test
    public void apachePropertyUtils() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        long startTime = System.currentTimeMillis();
        for (int i = 1; i <= count; i++) {
            // log.debug(i+"");
            TeacherEntity entity = new TeacherEntity();
            PropertyUtils.copyProperties(teacherDO, entity);
        }
        System.out.println("apachePropertyUtils time" + (System.currentTimeMillis() - startTime));
    }

    @Test
    public void fastjson() {
        long startTime = System.currentTimeMillis();
        for (int i = 1; i <= count; i++) {
            // log.debug(i+"");
            TeacherEntity entity = JSON.parseObject(JSON.toJSONString(teacherDO), TeacherEntity.class);
        }
        System.out.println("fastjson time" + (System.currentTimeMillis() - startTime));
    }

}

测试结果

tools/count 1000/次 10000/次 100000/次 1000000/次
手写 1ms 2ms 7ms 14ms
mapstruct 3ms 5ms 10ms 11ms
beanCopier 1ms 3ms 11ms 14ms
apachePropertyUtils 26ms 99ms 335ms 2438ms
springBeanUtils 91ms 108ms 180ms 511ms
apacheBeanUtils 107ms 213ms 598ms 4022ms
fastjson 146ms 274ms 402ms 653ms

相关文章

  • JAVA 对象转化

    前言 在我们的实际编码工作中会碰到很多对象转化的场景,尤其像DO、DTO、VO等对象的转化非常多,通常我们会选择第...

  • using

    JAVA md5 将json转化为java对象 转化xmlToJSONjson转xml 遍历map: 线程池线程池...

  • Java:对一个对象序列化和反序列化的简单实现

    名词解释 序列化:将Java对象转化成字节的过程 反序列化:将字节转化成Java对象的过程 字节:1字节(byte...

  • java对象序列化

    对象序列化是指将java对象转化成二进制流的过程。反之则是反序列化的过程。java中对象通过实现Serializa...

  • java几个对象转化方法

    1.int intValue() 以 int 类型返回该 Integer 的值。 2.static int par...

  • Json解析(使用Gson)

    Json的解析成 java 对象 Gson gson = new Gson(); // 将json 转化成 j...

  • java序列化和序列化ID的作用

    谈到java序列化其实大家都能说出一二, java对象序列化的意思就是将对象的状态转化成字节流,以后可以通过这些值...

  • Jackson 入门

    Jackson 入门 1. 快速参考 1.1 将java对象转化为json串(序列化), writeValue(....

  • Java末日第1篇

    问:谈谈你对 Java 序列化与反序列化理解? 答:序列化就是将对象转化为字节流,反序列化就是将字节流转化为对象,...

  • Java&Android 基础知识梳理(2) - 序列化

    一、Parcelable和Serializable 对象的序列化是把Java对象转化为字节序列并存储至一个存储媒介...

网友评论

      本文标题:JAVA 对象转化

      本文链接:https://www.haomeiwen.com/subject/jasbsltx.html