Java对象属性拷贝工具对比分析

1. 对象属性拷贝概述

在开发中经常遇到对象属性拷贝功能,而对象属性拷贝方式很多,比如手动set赋值,虽然麻烦,但是性能是最好的,其次MapStruct也是通过预编译完成,效率等同手动set,但是这两种相较于一些工具类稍微麻烦一些,一些常用的工具类方便简单,而且效率也相对不错,比如SpringBeanUtils,CgLib,hutoolBeanUtil效率功能都很不错,而且没有第三方依赖,非常干净好用,而Apache的BeanUtils与PropertyUtils虽然使用方便,但是效率不高,很少使用了。

对于手动set实现对象拷贝功能介绍一款idea插件可自动完成两个对象之间set/get值,GenerateAllSetter,IDEA 一键生成所有setter方法,alt+ctrl+s 快捷键进入设置,在Plugins中搜索GenerateAllSetter安装插件,然后重启idea即可。
使用方法,光标置于对象要转换的对象之上,alt+enter会出现快捷选项Generate all setter即可。

2. 对象属性拷贝工具

2.1 拷贝工具对比

拷贝工具使用效率
Spring BeanUtils使用方便,效率中等
Cglib BeanCopier使用方便,效率最高
Apache BeanUtils使用方便,效率低,原因Apache BeanUtils力求做的完美,做了很多校验,兼容,日志打印等导致性能下降。
Apache PropertyUtils使用方便,效率低
Hutool BeanUtil使用方便,封装完善,效率较高

2.2 拷贝工具验证

对比结果,仅供参考,当然耗时因素很多,机器配置环境等。

工具类100次消耗时间1000次消耗时间10000次消耗时间100000次消耗时间1000000次消耗时间
Spring BeanUtils30714371651518
Cglib BeanCopier323102780
Apache BeanUtils28371847677076
Apache PropertyUtils2161018578693

3. 对象属性拷贝实现

3.1 集合对象拷贝

BeanUtils使用instantiateClass初始化对象注意:
必须保证初始化类必须有public默认无参数构造器,注意初始化内部类时,内部类必须是静态的,否则报错!

import cn.hutool.core.collection.CollectionUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;

import java.util.List;

/**
 * 对象属性拷贝
 *
 * @author zrj
 * @since 2021/7/29
 **/
@Slf4j
public class BeanUtil {
    /**
     * 对象属性拷贝
     * 将源对象的属性拷贝到目标对象
     *
     * @param source 源对象
     * @param target 目标对象
     */
    public static void copyProperties(Object source, Object target) {
        try {
            BeanUtils.copyProperties(source, target);
        } catch (BeansException e) {
            log.error("BeanUtil property copy  failed :BeansException", e);
        } catch (Exception e) {
            log.error("BeanUtil property copy failed:Exception", e);
        }
    }

    /**
     * @param input 输入集合
     * @param clzz  输出集合类型
     * @param <E>   输入集合类型
     * @param <T>   输出集合类型
     * @return 返回集合
     */
    public static <E, T> List<T> convertList2List(List<E> input, Class<T> clzz) {
        List<T> output = Lists.newArrayList();
        if (CollectionUtil.isEmpty(input)) {
            return output;
        }

        input.forEach(source -> {
            T target = BeanUtils.instantiateClass(clzz);
            BeanUtils.copyProperties(source, target);
            output.add(target);
        });
        return output;
    }
}

3.2 对象拷贝验证

BeanMapperUtils

package com.jerry.unit.bean;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;

import java.util.ArrayList;
import java.util.List;

/**
 * 对象属性拷贝工具
 * 1、Spring BeanUtils:效率其次
 * 2、Cglib BeanCopier:效率最高
 * 3、Apache BeanUtils:效率低,原因Apache BeanUtils力求做的完美,做了很多校验,兼容,日志打印等导致性能下降。
 * 4、Apache PropertyUtils:效率低
 * 5、HutoolBeanUtil :使用方便,效率比较高
 *
 * @author zrj
 * @since 2022/3/26
 **/
@Slf4j
public class BeanMapperUtils {
    /**
     * SpringBeanUtils对象属性拷贝
     *
     * @param source 源对象
     * @param target 目标对象
     */
    public static void mappingBeanBySpringBeanUtils(Object source, Object target) {
        try {
            BeanUtils.copyProperties(source, target);
        } catch (Exception e) {
            log.error("SpringBeanUtils Mapping Failed,ErrorMessage:{},{}", e.getMessage(), e);
        }
    }

    /**
     * CglibBeanCopier对象属性拷贝
     *
     * @param source       源对象,PersonDO.class
     * @param target       目标对象,PersonDTO.class
     * @param useConverter
     * @param var1         源对象,personDO
     * @param var2         目标对象,new PersonDTO()
     * @param var3
     */
    public static void mappingBeanByCglibBeanCopier(Class source, Class target, boolean useConverter, Object var1, Object var2, Converter var3) {
        try {
            BeanCopier copier = BeanCopier.create(source, target, useConverter);
            copier.copy(var1, var2, var3);
        } catch (Exception e) {
            log.error("CglibBeanCopier Mapping Failed,ErrorMessage:{},{}", e.getMessage(), e);
        }
    }

    /**
     * ApacheBeanUtils对象属性拷贝
     *
     * @param dest 目标对象
     * @param orig 源对象
     */
    private void mappingBeanByApacheBeanUtils(Object dest, Object orig) {
        try {
            org.apache.commons.beanutils.BeanUtils.copyProperties(dest, orig);
        } catch (Exception e) {
            log.error("ApacheBeanUtils Mapping Failed,ErrorMessage:{},{}", e.getMessage(), e);
        }
    }

    /**
     * ApachePropertyUtils进行属性拷贝(几乎很少使用)
     *
     * @param dest 目标对象
     * @param orig 源对象
     */
    private void mappingBeanByApachePropertyUtils(Object dest, Object orig) {
        try {
            org.apache.commons.beanutils.PropertyUtils.copyProperties(dest, orig);
        } catch (Exception e) {
            log.error("ApachePropertyUtils Mapping Failed,ErrorMessage:{},{}", e.getMessage(), e);
        }
    }

    /**
     * HutoolBeanUtil进行属性拷贝(几乎很少使用)
     *
     * @param source     源Bean对象
     * @param target     目标Bean对象
     * @param ignoreCase 是否忽略大小写
     */
    private void mappingBeanByHutoolBeanUtil(Object source, Object target, boolean ignoreCase) {
        try {
            BeanUtil.copyProperties(source, target, ignoreCase);
        } catch (Exception e) {
            log.error("HutoolBeanUtil Mapping Failed,ErrorMessage:{},{}", e.getMessage(), e);
        }
    }

    /**
     * SpringBeanUtils集合对象属性拷贝
     *
     * @param input 输入集合
     * @param clzz  输出集合类型
     * @param <E>   输入集合类型
     * @param <T>   输出集合类型
     * @return 返回集合
     */
    public static <E, T> List<T> mappingListSpringBeanUtils(List<E> input, Class<T> clzz) {
        List<T> output = new ArrayList<>();
        if (CollectionUtil.isEmpty(input)) {
            return output;
        }
        input.forEach(source -> {
            T target = BeanUtils.instantiateClass(clzz);
            BeanUtils.copyProperties(source, target);
            output.add(target);
        });
        return output;
    }
}

BeanMapperUtilsTest

package com.jerry.unit.bean;

import cn.hutool.core.bean.BeanUtil;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.util.StopWatch;

import java.lang.reflect.InvocationTargetException;

import static org.apache.commons.beanutils.BeanUtils.copyProperties;

/**
 * 对象属性拷贝工具测试类
 * 1、Spring BeanUtils:效率其次
 * 2、Cglib BeanCopier:效率最高
 * 3、Apache BeanUtils:效率低,原因Apache BeanUtils力求做的完美,做了很多校验,兼容,日志打印等导致性能下降。
 * 4、Apache PropertyUtils:效率低
 * 5、Dozer
 * 使用场景:DO,DTO,VO,PO,BO等场景装换的时候通过对象属性拷贝提高开发效率,简化代码,
 * 但是同时也带来了隐藏拷贝了那些属性,没有直接转换更为直接,按需使用吧
 *
 * @author zrj
 * @since 2022/3/26
 **/
public class BeanMapperUtilsTest {
    public static void main(String[] args) throws Exception {
        //构建对象
        PersonDO personDO = PersonDO.builder().id(20220326).name("jerry").age(6).build();

        BeanMapperUtilsTest mapperTest = new BeanMapperUtilsTest();

        System.out.println("------mappingBySelf-------");
        mapperTest.mappingBySelf(personDO, 100);
        mapperTest.mappingBySelf(personDO, 1000);
        mapperTest.mappingBySelf(personDO, 10000);
        mapperTest.mappingBySelf(personDO, 100000);
        mapperTest.mappingBySelf(personDO, 1000000);

        System.out.println("------mappingBeanByHutoolBeanUtil-------");
        mapperTest.mappingBeanByHutoolBeanUtil(personDO, 100);
        mapperTest.mappingBeanByHutoolBeanUtil(personDO, 1000);
        mapperTest.mappingBeanByHutoolBeanUtil(personDO, 10000);
        mapperTest.mappingBeanByHutoolBeanUtil(personDO, 100000);
        mapperTest.mappingBeanByHutoolBeanUtil(personDO, 1000000);

        System.out.println("------mappingBySpringBeanUtils-------");
        mapperTest.mappingBySpringBeanUtils(personDO, 100);
        mapperTest.mappingBySpringBeanUtils(personDO, 1000);
        mapperTest.mappingBySpringBeanUtils(personDO, 10000);
        mapperTest.mappingBySpringBeanUtils(personDO, 100000);
        mapperTest.mappingBySpringBeanUtils(personDO, 1000000);

        System.out.println("------mappingByCglibBeanCopier-------");
        mapperTest.mappingByCglibBeanCopier(personDO, 100);
        mapperTest.mappingByCglibBeanCopier(personDO, 1000);
        mapperTest.mappingByCglibBeanCopier(personDO, 10000);
        mapperTest.mappingByCglibBeanCopier(personDO, 100000);
        mapperTest.mappingByCglibBeanCopier(personDO, 1000000);

        System.out.println("------mappingByApachePropertyUtils-------");
        mapperTest.mappingByApachePropertyUtils(personDO, 100);
        mapperTest.mappingByApachePropertyUtils(personDO, 1000);
        mapperTest.mappingByApachePropertyUtils(personDO, 10000);
        mapperTest.mappingByApachePropertyUtils(personDO, 100000);
        mapperTest.mappingByApachePropertyUtils(personDO, 1000000);

        System.out.println("------mappingByApacheBeanUtils-------");
        mapperTest.mappingByApacheBeanUtils(personDO, 100);
        mapperTest.mappingByApacheBeanUtils(personDO, 1000);
        mapperTest.mappingByApacheBeanUtils(personDO, 10000);
        mapperTest.mappingByApacheBeanUtils(personDO, 100000);
        mapperTest.mappingByApacheBeanUtils(personDO, 1000000);
    }

    /**
     * 手动进行属性拷贝
     */
    private void mappingBySelf(PersonDO personDO, int times) {
        StopWatch stopwatch = new StopWatch();
        stopwatch.start();

        for (int i = 0; i < times; i++) {
            PersonDTO personDTO = new PersonDTO();
            personDTO.setName(personDO.getName());
            personDTO.setAge(personDO.getAge());
        }

        stopwatch.stop();
        System.out.println("mappingBySelf cost " + times + " :" + stopwatch.getTotalTimeMillis());
    }

    /**
     * 手动进行属性拷贝
     */
    private void mappingBeanByHutoolBeanUtil(PersonDO personDO, int times) {
        StopWatch stopwatch = new StopWatch();
        stopwatch.start();

        for (int i = 0; i < times; i++) {
            PersonDTO personDTO = new PersonDTO();
            BeanUtil.copyProperties(personDO, personDTO, false);
        }

        stopwatch.stop();
        System.out.println("mappingBeanByHutoolBeanUtil cost " + times + " :" + stopwatch.getTotalTimeMillis());
    }

    /**
     * Spring BeanUtils进行属性拷贝
     */
    private void mappingBySpringBeanUtils(PersonDO personDO, int times) {
        StopWatch stopwatch = new StopWatch();
        stopwatch.start();

        for (int i = 0; i < times; i++) {
            PersonDTO personDTO = new PersonDTO();
            BeanUtils.copyProperties(personDO, personDTO);
        }

        stopwatch.stop();
        System.out.println("mappingBySpringBeanUtils cost " + times + " :" + stopwatch.getTotalTimeMillis());
    }

    /**
     * Cglib BeanCopier进行属性拷贝
     */
    private void mappingByCglibBeanCopier(PersonDO personDO, int times) {
        StopWatch stopwatch = new StopWatch();

        stopwatch.start();

        for (int i = 0; i < times; i++) {
            PersonDTO personDTO = new PersonDTO();
            BeanCopier copier = BeanCopier.create(PersonDO.class, PersonDTO.class, false);
            copier.copy(personDO, personDTO, null);
        }

        stopwatch.stop();

        System.out.println("mappingByCglibBeanCopier cost " + times + " :" + stopwatch.getTotalTimeMillis());
    }

    /**
     * Apache BeanUtils进行属性拷贝
     */
    private void mappingByApacheBeanUtils(PersonDO personDO, int times)
            throws InvocationTargetException, IllegalAccessException {
        StopWatch stopwatch = new StopWatch();
        stopwatch.start();
        for (int i = 0; i < times; i++) {
            PersonDTO personDTO = new PersonDTO();
            copyProperties(personDTO, personDO);
        }
        stopwatch.stop();
        System.out.println("mappingByApacheBeanUtils cost " + times + " :" + stopwatch.getTotalTimeMillis());
    }

    /**
     * Apache PropertyUtils进行属性拷贝
     * 无人问津
     */
    private void mappingByApachePropertyUtils(PersonDO personDO, int times)
            throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        StopWatch stopwatch = new StopWatch();
        stopwatch.start();
        for (int i = 0; i < times; i++) {
            PersonDTO personDTO = new PersonDTO();
            PropertyUtils.copyProperties(personDTO, personDO);
        }
        stopwatch.stop();
        System.out.println("mappingByApachePropertyUtils cost " + times + " :" + stopwatch.getTotalTimeMillis());
    }
}

PersonDO & PersonDTO

/**
 * 人员数据对象
 *
 * @author zrj
 * @since 2022/3/26
 **/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PersonDO {
    private Integer id;
    private String name;
    private Integer age;
}

/**
 * 人员传输对象
 *
 * @author zrj
 * @since 2022/3/26
 **/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PersonDTO {
    private String name;
    private Integer age;
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值