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

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

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

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

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

image.png

  • 添加依赖


ma.glasnost.orika
orika-core
1.5.4

定义工具类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 CACHE_MAPPER = new ConcurrentHashMap<>();

    private final MapperFacade mapper;

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


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


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

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


    /**
     * 字段不一致集合转换函数 (需要字段映射)
     *
     * @param sourceEntityList 源实体集合
     * @param targetClass      目标类对象
     * @param refMap           配置源类与目标类不同字段名映射
     * @param               源泛型
     * @param               目标泛型
     * @return 目标实体集合
     */
    public static  List convertList(List sourceEntityList, Class targetClass, Map 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  void register(Class source, Class

target,Map refMap){ if (CollectionUtils.isEmpty(refMap)) { FACTORY.classMap(source, target).byDefault().register(); } else { ClassMapBuilder classMapBuilder = FACTORY.classMap(source, target); refMap.forEach(classMapBuilder::field); classMapBuilder.byDefault().register(); } } /** * 属性名称一致可用 * @param source 源数据 * @param target 目标对象 * @return OrikaUtils */ private static OrikaUtils classMap(Class source, Class

target) { return classMap(source, target, null); } /** * 属性名称不一致可用 * * @param source 原对象 * @param target 目标对象 * @return OrikaUtils */ private static synchronized OrikaUtils classMap(Class source, Class

target, Map 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 P map(V source, Class

target) { return mapper.map(source, target); } /** * 复制List * @param source 源对象 * @param target 目标对象 * @return P */ private List

mapAsList(List source, Class

target) { return CollectionUtils.isEmpty(source) ? Collections.emptyList() : mapper.mapAsList(source, target); } }

 实例应用 Service接口实现

  • List dtoList = hjyCommunityMapper.queryList(community); 调用mapper接口的查询数据方法,此时的接收类型是DTO
  • 利用stream流方式将dto数据集合,调用OrikaUtils 工具类的转换方法convert()    转换成HjyCommunityVo 作为service接口方法的返回类型 
 /**
      * 获取小区下拉列表
      * @param community
      * @return: com.msb.hjycommunity.community.domain.vo.HjyCommunityVo
      */
 List queryPullDown(HjyCommunity community);
 
 
 
 @Override
 public List queryPullDown(HjyCommunity community) {
 
     List dtoList = hjyCommunityMapper.queryList(community);
 
     List 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  List castList(Object obj) {
        String json = JsonUtils.objectToJson(obj);
        return JSONObject.parseObject(json, new TypeReference>(){});
    }

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

你可能感兴趣的:(业务场景实例问题,Java,Spring,boot,OrikaUtils,spring,boot,java,BeanUtils)