【业务功能篇51】对象复制的三种方式 工具类Orika、反射、BeanUtils浅拷贝

业务场景: 设计规范前提下,我们数据层传输承载的是用DTO,而到控制层返回给前端会对应定义一个VO对象,比如是一个问题单数据集合list<DTO>,数据层接收的是DTO对对象,到控制层接收后需要转换成list<VO>,这里就涉及到要转换对象数据

对象复制的类库工具有很多 Orika是目前性能最强,同时也最流行的对象映射工具,Orika底层采用了javassist类库生成Bean映射的字节码,之后直接加载执行生成的字节码文件,在速度上比使用反射进行赋值会快很多。

阿里巴巴开发手册上强制规定避免使用Apache BeanUtils

  • 原因在于 Apache BeanUtils底层源码为了追求完美,加了过多的包装,使用了很多反射,做了很多校验,所以导致性能较差

image.png

  • 添加依赖
<!-- 对象属性拷贝 -->
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.5.4</version>
</dependency>

定义工具类OrikaUtils

package com.hjycommunity.common.utils;

import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.ClassMapBuilder;
import ma.glasnost.orika.metadata.Type;
import ma.glasnost.orika.metadata.TypeFactory;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 对象复制工具类
 **/
public class OrikaUtils {

    //构造一个MapperFactory
    private static final MapperFactory FACTORY = new DefaultMapperFactory.Builder().build();

    /**
     * 缓存实例集合
     */
    private static final Map<String, MapperFacade> CACHE_MAPPER = new ConcurrentHashMap<>();

    private final MapperFacade mapper;

    public OrikaUtils(MapperFacade mapper) {
        this.mapper = mapper;
    }


    /**
     * 字段一致实体转换函数
     * @param sourceEntity 源实体
     * @param targetClass  目标类对象
     * @param <S>          源泛型
     * @param <T>          目标泛型
     * @return 目标实体
     */
    public static <S, T> T convert(S sourceEntity, Class<T> targetClass) {
        return convert(sourceEntity, targetClass, null);
    }


    /**
     * 字段不一致实体转换函数(需要字段映射)
     * @param sourceEntity 源实体
     * @param targetClass  目标类对象
     * @param refMap       配置源类与目标类不同字段名映射
     * @param <S>          源泛型
     * @param <T>          目标泛型
     * @return 目标实体
     */
    public static <S, T> T convert(S sourceEntity, Class<T> targetClass, Map<String, String> refMap) {
        if (sourceEntity == null) {
            return null;
        }
        return classMap(sourceEntity.getClass(), targetClass, refMap).map(sourceEntity, targetClass);
    }

    /**
     * 字段一致集合转换函数
     * @param sourceEntityList 源实体集合
     * @param targetClass      目标类对象
     * @param <S>              源泛型
     * @param <T>              目标泛型
     * @return 目标实体集合
     */
    public static <S, T> List<T> convertList(List<S> sourceEntityList, Class<T> targetClass) {
        return convertList(sourceEntityList, targetClass, null);
    }


    /**
     * 字段不一致集合转换函数 (需要字段映射)
     *
     * @param sourceEntityList 源实体集合
     * @param targetClass      目标类对象
     * @param refMap           配置源类与目标类不同字段名映射
     * @param <S>              源泛型
     * @param <T>              目标泛型
     * @return 目标实体集合
     */
    public static <S, T> List<T> convertList(List<S> sourceEntityList, Class<T> targetClass, Map<String, String> refMap) {
        if (sourceEntityList == null) {
            return null;
        }
        if (sourceEntityList.size() == 0) {
            return new ArrayList<>(0);
        }
        return classMap(sourceEntityList.get(0).getClass(), targetClass, refMap).mapAsList(sourceEntityList, targetClass);
    }


    /**
     * 字段属性转换
     * @param source 源类
     * @param target 目标类
     * @param refMap 属性转换
     */
    public static <V, P> void register(Class<V> source, Class<P> target,Map<String, String> refMap){
        if (CollectionUtils.isEmpty(refMap)) {
            FACTORY.classMap(source, target).byDefault().register();
        } else {
            ClassMapBuilder<V, P> classMapBuilder = FACTORY.classMap(source, target);
            refMap.forEach(classMapBuilder::field);
            classMapBuilder.byDefault().register();
        }
    }

    /**
     * 属性名称一致可用
     * @param source 源数据
     * @param target 目标对象
     * @return OrikaUtils
     */
    private static <V, P> OrikaUtils classMap(Class<V> source, Class<P> target) {
        return classMap(source, target, null);
    }

    /**
     * 属性名称不一致可用
     *
     * @param source 原对象
     * @param target 目标对象
     * @return OrikaUtils
     */
    private static synchronized <V, P> OrikaUtils classMap(Class<V> source, Class<P> target, Map<String, String> refMap) {
        String key = source.getCanonicalName() + ":" + target.getCanonicalName();
        if (CACHE_MAPPER.containsKey(key)) {
            return new OrikaUtils(CACHE_MAPPER.get(key));
        }
        register(source,target,refMap);
        MapperFacade mapperFacade = FACTORY.getMapperFacade();
        CACHE_MAPPER.put(key, mapperFacade);

        return new OrikaUtils(mapperFacade);
    }



    /**
     * Orika复制对象
     * @param source 源数据
     * @param target 目标对象
     * @return target
     */
    private <V, P> P map(V source, Class<P> target) {
        return mapper.map(source, target);
    }

    /**
     * 复制List
     * @param source 源对象
     * @param target 目标对象
     * @return P
     */
    private <V, P> List<P> mapAsList(List<V> source, Class<P> target) {
        return CollectionUtils.isEmpty(source) ? Collections.emptyList() : mapper.mapAsList(source, target);
    }

}

 实例应用 Service接口实现

  • List<HjyCommunityDto> dtoList = hjyCommunityMapper.queryList(community); 调用mapper接口的查询数据方法,此时的接收类型是DTO
  • 利用stream流方式将dto数据集合,调用OrikaUtils 工具类的转换方法convert()    转换成HjyCommunityVo 作为service接口方法的返回类型 
 /**
      * 获取小区下拉列表
      * @param community
      * @return: com.msb.hjycommunity.community.domain.vo.HjyCommunityVo
      */
 List<HjyCommunityVo> queryPullDown(HjyCommunity community);
 
 
 
 @Override
 public List<HjyCommunityVo> queryPullDown(HjyCommunity community) {
 
     List<HjyCommunityDto> dtoList = hjyCommunityMapper.queryList(community);
 
     List<HjyCommunityVo> voList = dtoList.stream().map(dto -> {
         //对象拷贝
         HjyCommunityVo communityVo = OrikaUtils.convert(dto, HjyCommunityVo.class);
         return communityVo;
     }).collect(Collectors.toList());
 
 
     return voList;
 }

 反射方式

  • 通过反射给 caseProblemListVO对象属性赋值
  • 这里举例通过map循环赋值,注意目标对象与源对象的字段需要保存一致
//是获取caseProblemListVO实体对象的某一个属性,entry.getKey().trim()  就是某个属性的名字
Field field = caseProblemListVO.getClass().getDeclaredField(entry.getKey().trim());

//设置可赋值属性变量
field.setAccessible(true);

// 将entry.getValue() 赋值给caseProblemListVO类当前对象的属性变量 
field.set(caseProblemListVO, entry.getValue());

 BeanUtils.copyProperties浅拷贝

  • 利用 spring的内置API :BeanUtils.copyProperties ,传参(源端对象,目标对象),即可实现复制到目标对象值  这里也是需要属性名一致


package com.utils;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;

import org.springframework.beans.BeanUtils;

import java.util.List;
import java.util.Map;

/**
 * MyBeanUtils
 * 
 */
/**
 * 对象之间的转换工具类
 *
 * @see JsonUtils
 */
public class MyBeanUtils {

    // 浅拷贝

    /**
     * shallowCopy
     * 
     * @param source source
     * @param target target
     */
    public static void shallowCopy(Object source, Object target) {
        BeanUtils.copyProperties(source, target);
    }

    /**
     * 强转为List(常用于强转前端参数)
     *
     * @param obj 实际类型为List的Object
     */
    public static <T> List<T> castList(Object obj) {
        String json = JsonUtils.objectToJson(obj);
        return JSONObject.parseObject(json, new TypeReference<List<T>>(){});
    }

    /**
     * 强转为Map(常用于强转前端参数)
     *
     * @param obj 实际类型为Map的Object
     */
    public static <K,V> Map<K,V> castMap(Object obj) {
        String json = JsonUtils.objectToJson(obj);
        return JSONObject.parseObject(json, new TypeReference<Map<K,V>>(){});
    }
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值