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

news2024/11/6 7:17:25

业务场景: 设计规范前提下,我们数据层传输承载的是用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>>(){});
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/788466.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目中如何使用文件IO?【大学学了好几门语言都有IO,到底怎么用?】

import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException;public class TestFile {public static void main(String[] args) throws IOException {// 通过这个简单的程序, 把一个文件的内容读取出…

Docker Compose 容器编排

Docker compose Docker compose 实现单机容器集群编排管理&#xff08;使用一个模板文件定义多个应用容器的启动参数和依赖关系&#xff0c;并使用docker compose来根据这个模板文件的配置来启动容器&#xff09; 通俗来说就是把之前的多条docker run启动容器命令 转换为docker…

关于云服务器ECS、宝塔的安装配置以及图床的使用

一、阿里云服务器的申请以及宝塔的安装 安装配置服务器的原理&#xff1a; step1&#xff1a;地址栏输入阿里云服务器官网地址 step2&#xff1a;在首页依次点击以下内容&#xff1a; step3&#xff1a;选择立即购买&#xff0c;并填写以下内容&#xff1a; step4&#xff1a…

6.运算符

6.1赋值运算符 ➢已经学过的赋值运算符&#xff1a; ➢其他赋值运算符&#xff1a; 、-、*、/、% 6.2 一元运算符 众多的JavaScript的运算符可以根据所需表达式的个数, 分为一元运算符、二元运算符、三元运算符 ●二元运算符: 例&#xff1a;let num1020 ●一元运算符: 例…

Vue3输入框(Input)

APIs 参数说明类型默认值必传width输入框宽度string | number‘100%’falseaddonBefore设置前置标签string | slot‘’falseaddonAfter设置后置标签string | slot‘’falseallowClear可以点击清除图标删除内容booleanfalsefalsepassword是否启用密码框booleanfalsefalsedisabl…

CSS3 Flexbox

Flex 是 Flexible Box 的缩写&#xff0c;意为弹性盒子布局。 CSS3中一种新的布局模式&#xff1a;W3C在2009年提出的一种布局方案&#xff0c;一种当页面需要适应不同的屏幕大小以及设备类型时确保元素拥有恰当的行为的布局方式。其目的是提供一种更加有效的方式来对一个容器…

回收站不见了?正确的2个操作方法!

大家有没有遇到回收站不见了的情况啊&#xff1f;真的很崩溃&#xff0c;我误删了一个比较重要的文件&#xff0c;想在回收站中把它还原&#xff0c;才发现我的回收站不见了&#xff01;这可咋整啊&#xff1f; 回收站是电脑操作系统中一个比较重要的功能&#xff0c;它可以帮助…

Ceph简介和特性

Ceph是一个多版本存储系统&#xff0c;它把每一个待管理的数据流(例如一个文件) 切分为一到多个固定大小的对象数据&#xff0c;并以其为原子单元完成数据存取。 对象数据的底层存储服务是由多个主机 (host) 组成的存储集群&#xff0c;该集群也被称之为 RADOS (ReliableAutoma…

DS18B20的原理及实例代码(51单片机、STM32单片机)

一、DS18B20介绍 DS18B20数字温度传感器是DALLAS公司生产的单总线器件&#xff0c;用它来组成一个测温系统具有线路简单&#xff0c;体积小&#xff0c;在一根通信线上可以挂很多这样的数字温度传感器&#xff0c;十分方便。 温度传感器种类众多&#xff0c;应用在高精度、高可…

快排、二路归并疑难杂症

蒟蒻小♥复习机试&#xff0c;记录一些疑点和注意点。 细节见代码注释 快排 快排中的边界条件判断需保证i<j&#xff0c;即满足label基准左边的数均小于右边的数。<的判断可能让子问题求解陷入死循环。 #include <iostream> #include <stdio.h> #include …

C++笔记之STL的sort使用第三个参数来自定义排序

C笔记之STL的sort使用第三个参数来自定义排序 code review! 文章目录 C笔记之STL的sort使用第三个参数来自定义排序1.方法一&#xff1a;使用比较函数(其实是使用函数指针)作为std::sort()的第三个参数来排序2.方法二&#xff1a;使用lambda表达式作为std::sort()的第三个参数…

IO进程线程day1(2023.7.25)

一、Xmind整理&#xff1a; 什么是IO&#xff1a; 文件IO函数与标准IO函数&#xff1a; 二、课上练习&#xff1a; 练习1&#xff1a;标准IO函数的简单示例 scanf: if(OS Linux) {去调用Linux的文件IOread(); } else if(OS windows) {去调用windows的文件IOfread(); …

垃圾回收回收阶段算法

当成功区分出垃圾的对象和存活的对象后&#xff0c;下面就是要开始回收了&#xff0c;目前在JVM中的垃圾回收阶段的算法有三种&#xff1a;标记——复制算法、标记——清除算法、标记——压缩算法。 1.标记——复制算法 将可用内存按照容量分为大小相等的两块&#xff0c;每次…

国标GB28181视频监控平台EasyGBS视频无法播放,抓包返回ICMP是什么原因?

国标GB28181视频平台EasyGBS是基于国标GB/T28181协议的行业内安防视频流媒体能力平台&#xff0c;可实现的视频功能包括&#xff1a;实时监控直播、录像、检索与回看、语音对讲、云存储、告警、平台级联等功能。国标GB28181视频监控平台部署简单、可拓展性强&#xff0c;支持将…

【软件测试】webdriver常用API演示(Java+IDEA+chrome浏览器)

1.元素定位方法 对象的定位应该是自动化测试的核心&#xff0c;要想操作一个对象&#xff0c;首先应该识别这个对象。一个对象就是一个人一样&#xff0c;他会有各种的特征&#xff08;属性&#xff09;&#xff0c;如比我们可以通过一个人的身份证号&#xff0c;姓名&#xf…

centos(linux)手动配置ip地址

查看系统 查看ip 进入网卡配置的目录 [root178-119-30-16 ~]# cd /etc/sysconfig/network-scripts/ [root178-119-30-16 network-scripts]# ls ifcfg-ens192 ifdown ifdown-ippp ifdown-post ifdown-sit ifdown-tunnel ifup-bnep ifup-ipv6 ifup-plus…

C#代码实现状态机

状态机四要素 现态、条件、动作、次态 现态&#xff1a;是指当前所处的状态 条件&#xff1a;又称为“事件”&#xff0c;当一个条件被满足&#xff0c;将会触发一个动作&#xff0c;或者执行一次状态转移。 动作&#xff1a;条件满足后执行的动作。动作执行完毕后&#xff0…

JavaScript |(一)JavaScript简介及基本语法 | 尚硅谷JavaScript基础实战

学习来源&#xff1a;尚硅谷JavaScript基础&实战丨JS入门到精通全套完整版 文章目录 &#x1f4da;JavaScript简介&#x1f407; 实现&#x1f407;JavaScript的特点 &#x1f4da;基本知识&#x1f407;编写位置&#x1f525;方式一&#xff1a;在标签中写&#xff08;不推…

常见Vue Admin对比

作者&#xff1a;felix 个人博客 常见Vue Admin对比 Vue element admin - 老牌 admin 后台管理 求稳首选Antd Pro Vue - 背靠阿里&#xff0c;代码过硬&#xff0c;大型项目首选Vue vben admin - 宝藏后台管理 基于 Vue3 UI清新 功能扎实iView Admin - 老牌 admin 代码工程化 …

【【51单片机的AT24C02】】

51单片机的AT24C02 AT24C02 先介绍一下存储器的知识 这里的重点肯定是I2C总线 存储器 分为2种 一种是易失性存储器RAM 还有一种是非易失性存储器ROM RAM分为 SRAM 和 DRAM Mask ROM PROM EPROM E2PROM 这几个是一个家族的 从创建之后 不能修改 然后 只能写一次 再而发展成可擦…