【Java】Bean复制几种实现方法性能比较

【引言】

在项目开发过程中,会遇到将某个已有若干属性值的Bean复制给另一个具有相同属性名的Bean,除了一个一个属性去set方法之外,其实可以通过调用一些工具类下的方法直接copy,这样就避免了一个一个去set属性值,在简化了代码的基础上,也能快速地实现功能。

而实现这种功能的工具类并不只有一种,也听同事提到过如果数据量大的话,性能上会有影响。所以在工作空闲之时,自己写了个接口,测试了一下每种方法需要花费的时间,写个博客记录下来。

【方法】

  1. Apache 的BeanUtils
  2. Apache 的PropertyUtils
  3. Spring 的BeanUtils
  4. 反射工具包ReflectASM 自定义工具类

【测试】

测试的场景是在我们的商品应用服务中,写了一个接口,查询商品数据比较多的一个店铺,大概8万条左右,将查出来的实体ShopGoods数据拷贝到一个数据传输实体ShopGoodsDTO,结果返回的是拷贝所花费的时间,单位为毫秒。

下面是测试结果统计:

实现方法花费时间(ms)
Apache 的BeanUtils9431
Apache 的PropertyUtils8083
Spring 的BeanUtils4827
反射工具包ReflectASM 自定义工具类4777

【代码】

//利用反射工具包ReflectASM自定义工具类
public class BeanUtils {

    private static Map<Class, MethodAccess> methodMap = new HashMap<Class, MethodAccess>();

    private static Map<String, Integer> methodIndexMap = new HashMap<String, Integer>();

    private static Map<Class, List<String>> fieldMap = new HashMap<Class, List<String>>();

    public static void copyProperties(Object desc, Object orgi) {
        MethodAccess descMethodAccess = methodMap.get(desc.getClass());
        if (descMethodAccess == null) {
            descMethodAccess = cache(desc);
        }
        MethodAccess orgiMethodAccess = methodMap.get(orgi.getClass());
        if (orgiMethodAccess == null) {
            orgiMethodAccess = cache(orgi);
        }

        List<String> fieldList = fieldMap.get(orgi.getClass());
        for (String field : fieldList) {
            String getKey = orgi.getClass().getName() + "." + "get" + field;
            String setkey = desc.getClass().getName() + "." + "set" + field;
            Integer setIndex = methodIndexMap.get(setkey);
            if (setIndex != null) {
                int getIndex = methodIndexMap.get(getKey);
                // 参数一需要反射的对象
                // 参数二class.getDeclaredMethods 对应方法的index
                // 参数对三象集合
                descMethodAccess.invoke(desc, setIndex.intValue(),
                        orgiMethodAccess.invoke(orgi, getIndex));
            }
        }
    }

    // 单例模式
    private static MethodAccess cache(Object orgi) {
        synchronized (orgi.getClass()) {
            MethodAccess methodAccess = MethodAccess.get(orgi.getClass());
            Field[] fields = orgi.getClass().getDeclaredFields();
            List<String> fieldList = new ArrayList<String>(fields.length);
            for (Field field : fields) {
                if (Modifier.isPrivate(field.getModifiers())
                        && !Modifier.isStatic(field.getModifiers())) { // 是否是私有的,是否是静态的
                    // 非公共私有变量
                    String fieldName = StringUtils.capitalize(field.getName()); // 获取属性名称
                    int getIndex = methodAccess.getIndex("get" + fieldName); // 获取get方法的下标
                    int setIndex = methodAccess.getIndex("set" + fieldName); // 获取set方法的下标
                    methodIndexMap.put(orgi.getClass().getName() + "." + "get"
                            + fieldName, getIndex); // 将类名get方法名,方法下标注册到map中
                    methodIndexMap.put(orgi.getClass().getName() + "." + "set"
                            + fieldName, setIndex); // 将类名set方法名,方法下标注册到map中
                    fieldList.add(fieldName); // 将属性名称放入集合里
                }
            }
            fieldMap.put(orgi.getClass(), fieldList); // 将类名,属性名称注册到map中
            methodMap.put(orgi.getClass(), methodAccess);
            return methodAccess;
        }
    }

}
/**
 * 测试方法:将查出的商品实体数据利用不同工具包下的方法拷贝至一个新的传输实体
 * 商品数据量:八万左右
 */
public long costSeconds()  {
    List<ShopGoodsDTO> shopGoodsDTOList=new ArrayList<>();
    ShopGoodsExample shopGoodsExample = new ShopGoodsExample();
    shopGoodsExample.createCriteria().andStoreIdEqualTo(743);
    // 查询到的商品数据
    List<ShopGoods> shopGoodsList = shopGoodsMapper.selectByExample(shopGoodsExample);
    long costSeconds=0L;
    if(shopGoodsList!=null && shopGoodsList.size()>0){
        long startTime=System.currentTimeMillis();
        for (ShopGoods shopGoods:shopGoodsList) {
            ShopGoodsDTO shopGoodsDTO = new ShopGoodsDTO();
            BeanUtils.copyProperties(shopGoodsDTO,shopGoods);
            //org.springframework.beans.BeanUtils.copyProperties(shopGoods,shopGoodsDTO);
            //BeanUtils.copyProperties(shopGoodsDTO,shopGoods);
//                try {
//                    PropertyUtils.copyProperties(shopGoodsDTO,shopGoods);
//                } catch (NoSuchMethodException e) {
//                    e.printStackTrace();
//                }
            System.out.println(new Gson().toJson(shopGoodsDTO));
            shopGoodsDTOList.add(shopGoodsDTO);
        }
        long endTime=System.currentTimeMillis();
        costSeconds=endTime-startTime;
    }
    return costSeconds;
}

【总结】

通过以上测试可以看出,Apache下的工具包下的方法性能较差,和Spring工具包下的方法及利用反射工具包ReflectASM自定义工具类的方法相比,时间上是需要多花一倍的。

而Spring工具包下的方法与利用反射工具包ReflectASM自定义工具类的方法相比,时间上是差不多的。

所以个人感觉,我们需要实现此类功能的时候,直接利用Spring BeanUtils工具包下的copyProperties方法即可。需要注意的是Spring BeanUtils下的方法参数与Apache BeanUtils下的方法参数是相反的,在使用时不要将源实体与目标实体弄混了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值