Spring学习笔记(二)【CGLIB浅拷贝BeanCopier的使用和详解】

news2024/11/16 19:57:11

CGLIB浅拷贝BeanCopier的使用和详解

一、bean拷贝工具


bean拷贝工具类比较

常用的bean拷贝工具类当中,主要有Apache提供的beanUtils、Spring提供的beanUtils、Cglib提供的beanCopier,性能上分析如下表所示(该表来自网上的数据)

方法归属单次耗时[ms]100万次耗时[ms]平均耗时[ms]
BeanUtilscopyPropertiesApache0.31019630253.1400570.030253
BeanUtilscopyPropertiesSpring0.0720822155.7443780.002155
BeanCopiercopyCglib0.00507158.4152010.000058

上表当中可以发现三者性能:cglib > spring > hutool

本次所讲的内容是关于BeanCopier类的使用,当我们需要拷贝大量的数据,使用这个是最快的,而对于拷贝少量对象时,和其它的拷贝工具类速度也差不多,现在CGLIB也并入Spring,所以在Spring项目中可以直接使用它,不需要添加其他maven

  • 原依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

优点:

  • 拷贝速度快

缺点:

  • 无法实现null字段跳过
  • 每次使用都需要调用create函数,如果上述实验把create放到循环里,结果会变成5s
  • 无法实现复杂类型的拷贝(如List)

在使用他的时候,我们需要先创建一个BeanCopier对象,源代码如下

public static BeanCopier create(Class source, Class target, boolean useConverter) {
    Generator gen = new Generator();
    gen.setSource(source);
    gen.setTarget(target);
    gen.setUseConverter(useConverter);
    return gen.create();
}

create函数参数解析:

  • 第一个参数source:需要拷贝的对象
  • 第二个参数target:拷贝后的目标对象类型
  • 第三个参数useConverter:用户控制转换器,是否使用自定义的转换器

useConverter控制权限转换:

这个是用户控制转换器,如果设置为false,它会对拷贝的对象和被拷贝的对象的类型进行判断,如果类型不同就不会拷贝,如果要使他会拷贝,就需要设置为true,自己拿到控制权对其进行处理,一般情况下使用的都是false

二、自定义转换器演示


首先我们有两个类,属性都相同,但有一个类中其中一个属性类型与另外一个类对应的属性类型不一致,情况如下

@Data
public class User {
    private String username;
    private Integer password;
}
@Data
public class UserDTO {
    private String username;
    private String password;
}
  • 执行代码
User user = new User();
user.setUsername("vinjcent");
user.setPassword("123456");
UserDTO userDTO = BeanCopierUtils.copy(user, UserDTO.class);

System.out.println("源对象===>" + user);
System.out.println("拷贝对象===>" + userDTO);

从控制台结果可以看出,拷贝的对象的password是空的,因为类型不同

在这里插入图片描述

如果要不同类型都拷贝上去,就要设置useConverter的控制权设置为true,并且实现Convert类,重写代码如下(注意,这是全类属性进行转换

package com.vinjcent.api.utils;

import org.springframework.cglib.core.Converter;

/**
 * @author vinjcent
 * @description bean拷贝转换器(不推荐使用,尽量设计时保持类型一致)
 * @since 2023/3/28 15:06
 */
public class ConverterSetting implements Converter {

    /**
     * @param source  源对象值
     * @param target  目标类型
     * @param setFunc 实体类的set方法
     * @return
     */
    @Override
    public Object convert(Object source, Class target, Object setFunc) {
        if (source instanceof Enum) {
            return source;
        } else {
            return source.toString();
        }
    }
}

convert函数参数解析:

  • 第一个参数source:源类的值
  • 第二个参数target:目标类类型
  • 第三个参数setFunc:源类的set方法

使用自定义转换器convert为true的代码

    /**
     * 自定义类型转换器
     *
     * @param source    源对象
     * @param target    目标类
     * @param converter 转换器
     */
    public static void copy(Object source, Object target, Converter converter) {
        BeanCopier beanCopier = BeanCopier.create(source.getClass(), target.getClass(), true);
        beanCopier.copy(source, target, converter);
    }
  • 测试代码以及结果
User user = new User();
user.setUsername("vinjcent");
user.setPassword(123456);
UserDTO userDTO = BeanCopierUtils.copy(user, UserDTO.class, new ConverterSetting());
System.out.println("源对象===>" + user);
System.out.println("拷贝对象===>" + userDTO);

在这里插入图片描述

三、目标类值的覆盖


在使用bean拷贝过程中,假如需要将源对象的属性值拷贝给另外一个有相同属性的目标对象,那么会进行值得覆盖,如果目标对象当中存在源对象都没有的属性,则不会进行覆盖,否则进行覆盖

修改对应的代码,如下

  • UserDTO
@Data
public class UserDTO {
    private String username;
    private String password;
    private Double weight;
}
  • 测试用例与结果
User user = new User();
user.setUsername("vinjcent");
user.setPassword(123456);
UserDTO userDTO = new UserDTO("Totoro", "1008611", 55.55);
BeanCopierUtils.copy(user, userDTO);
System.out.println("源对象===>" + user);
System.out.println("拷贝对象===>" + userDTO);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IEzZF4LK-1681094474671)(E:\note\images\image-20230328170840576.png#pic_center)]

四、Map优化创建BeanCopier


由于每次使用工具类进行拷贝都需要调用create()函数创建一个BeanCopier对象,可以将创建过的BeanCopier实例可以放到缓存中,下次相同的转换可以直接获取,提升性能

在这里,以源类名 + “_” + 目标类名作为key,对应创建好的BeanCopier作为value

package com.vinjcent.api.utils;

import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author vinjcent
 * @description cglib包中的bean拷贝(性能优于Spring当中的BeanUtils)
 * @since 2023/3/28 14:22
 */
public class BeanCopierUtils {


    /**
     * 创建一个map来存储BeanCopier缓存
     */
    private static final Map<String, BeanCopier> BEAN_COPIER_MAP = new ConcurrentHashMap<>();

    /**
     * 深拷贝,我们可以直接传实例化的拷贝对象和被实例化的拷贝对象进行深拷贝
     *
     * @param source 源对象
     * @param target 目标类
     */
    private static void copy(Object source, Object target) {
        // 获取当前两者转换map对应的key
        String key = getKey(source, target);
        BeanCopier beanCopier;
        // 判断键是否存在,不存在就将BeanCopier插入到map里,存在就直接获取
        if (!BEAN_COPIER_MAP.containsKey(key)) {
            // 参数1: 源对象类   参数2: 目标对象类   参数3: 是否使用自定义转换器
            beanCopier = BeanCopier.create(source.getClass(), target.getClass(), false);
            BEAN_COPIER_MAP.put(key, beanCopier);
        } else {
            beanCopier = BEAN_COPIER_MAP.get(key);
        }
        // 自定义转换器可在copy函数当中的第三个参数设置
        beanCopier.copy(source, target, null);
    }

    /**
     * 深拷贝
     *
     * @param source 源对象
     * @param target 目标类
     * @param <T>    目标类型
     * @return 单个目标类
     */
    public static <T> T copy(Object source, Class<T> target) {
        // 如果源对象为空,结束
        if (source == null) {
            return null;
        }
        // 用来判断目标类型空指针异常
        Objects.requireNonNull(target);
        T result = null;
        try {
            result = target.newInstance();
            copy(source, result);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * List深拷贝
     *
     * @param sources 源集合
     * @param target  目标类
     * @param <S>     源类型
     * @param <T>     目标类型
     * @return 目标类集合
     */
    public static <S, T> List<T> copyList(List<S> sources, Class<T> target) {
        // 用来判断目标类型空指针异常
        Objects.requireNonNull(target);
        return sources.stream().map(src -> copy(src, target)).collect(Collectors.toList());

    }

    /**
     * 自定义类型转换器
     *
     * @param source    源对象
     * @param target    目标类
     * @param converter 转换器
     */
    private static void copy(Object source, Object target, Converter converter) {
        if (!Objects.isNull(converter)) {
            BeanCopier beanCopier = BeanCopier.create(source.getClass(), target.getClass(), true);
            beanCopier.copy(source, target, converter);
        } else {
            copy(source, target);
        }
    }

    /**
     * 自定义类型转换器
     *
     * @param source    源对象
     * @param target    目标类
     * @param converter 转换器
     * @param <T>       目标类型
     * @return 单个目标类
     */
    public static <T> T copy(Object source, Class<T> target, Converter converter) {
        // 如果源对象为空,结束
        if (source == null) {
            return null;
        }
        // 用来判断目标类型空指针异常
        Objects.requireNonNull(target);
        T result = null;
        try {
            result = target.newInstance();
            copy(source, result, converter);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取Map中的Key
     *
     * @param source 源对象
     * @param target 目标类
     * @return 源对象与目标类名字的拼接
     */
    private static String getKey(Object source, Object target) {
        return source.getClass().getName() + "_" + target.getClass().getName();
    }

}

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

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

相关文章

探索Apache Hudi核心概念 (3) - Compaction

Compaction是MOR表的一项核心机制&#xff0c;Hudi利用Compaction将MOR表产生的Log File合并到新的Base File中。本文我们会通过Notebook介绍并演示Compaction的运行机制&#xff0c;帮助您理解其工作原理和相关配置。 1. 运行 Notebook 本文使用的Notebook是&#xff1a;《A…

此战成硕,我成功上岸西南交通大学了~~~

友友们&#xff0c;好久不见&#xff0c;很长时间没有更一个正式点的文章了&#xff01; 是因为我在去年年底忙着准备初试&#xff0c;今年年初在准备复试&#xff0c;直到3月底拟录取后&#xff0c;终于可以写下这篇上岸贴&#xff0c;和大家分享一下考研至上岸的一个过程 文章…

springboot+thymeleaf实现发Html邮件自由

2019年&#xff0c;我刚接触测试架构和测试开发类的工作时&#xff0c;经常会有自动化发邮件的功能&#xff0c;大都是从各个平台自动化统计一些数据出来&#xff0c;每周定时发一封邮件给领导交差&#xff0c;回过头来再看看我发的邮件&#xff0c;不美观&#xff0c;不专业。…

Android Jetpack:现代化Android开发的利器

Android Jetpack&#xff1a;现代化Android开发的利器 引言 随着移动应用的快速发展和用户体验的不断提升&#xff0c;现代化的Android应用开发变得愈发复杂和多样化。为了提高开发效率、简化代码、加速应用迭代&#xff0c;Google推出了Android Jetpack组件&#xff0c;成为现…

springboot零基础到项目实战

推荐教程&#xff1a; springboot零基础到项目实战 SpringBoot这门技术课程所包含的技术点其实并不是很多&#xff0c;但是围绕着SpringBoot的周边知识&#xff0c;也就是SpringBoot整合其他技术&#xff0c;这样的知识量很大&#xff0c;例如SpringBoot整合MyBatis等等。因此…

gitlab-ce升级方法

Centos7升级gitlab-ce&#xff1a; 1、记录当前版本 在升级前一定要做好备份&#xff0c;记录自己当前的gitlab-ce的版本&#xff1a; yum list | grep gitlab-ce 2、编辑/etc/gitlab/gitlab.rb文件&#xff1a; 1&#xff09;将下面几行注释取消。 说明&#xff1a; 1&am…

pytorch进阶学习(二):使用DataLoader读取自己的数据集

上一节使用的是官方数据集fashionminist进行训练&#xff0c;这节课使用自己搜集的数据集来进行数据的获取和训练。所需资源教学视频&#xff1a;https://www.bilibili.com/video/BV1by4y1b7hX/?spm_id_from333.1007.top_right_bar_window_history.content.click&vd_sourc…

建立数据驱动,关键字驱动和混合Selenium框架这些你了解吗

一、什么是Selenium框架&#xff1f; Selenium框架是一种代码结构&#xff0c;用于简化代码维护和提高代码可读性。框架涉及将整个代码分成较小的代码段&#xff0c;以测试特定的功能。 该代码的结构使得“数据集”与实际的“测试用例”分开&#xff0c;后者将测试Web应用程序…

【PyTorch】第八节:数据的预处理

作者&#x1f575;️‍♂️&#xff1a;让机器理解语言か 专栏&#x1f387;&#xff1a;PyTorch 描述&#x1f3a8;&#xff1a;PyTorch 是一个基于 Torch 的 Python 开源机器学习库。 寄语&#x1f493;&#xff1a;&#x1f43e;没有白走的路&#xff0c;每一步都算数&#…

【NVIDIA GPU 入门】综述

系列文章目录 文章目录系列文章目录前言一、概述二、GPU架构基础2.1 GPU概述2.2 GPU的架构2.3 自主查询GPU相关信息三、CUDA编程概念3.1 CUDA线程模型3.1 线程层次结构1.引入库2.读入数据总结参考文献前言 GPU作为机器学习的基础运算设备&#xff0c;基本上是无人不知无人不晓。…

【bsauce读论文】PSPRAY-基于时序侧信道的Linux内核堆利用技术

会议&#xff1a;USENIX Security’23 作者&#xff1a;来自 Seoul National University 的 Yoochan Lee、Byoungyoung Lee 等人。 主要内容&#xff1a;由于Linux内核的堆分配器SLUB开启的freelist随机化保护&#xff0c;所以堆相关的内核漏洞利用成功率较低&#xff08;平均…

BEV(一)---lift splat shoot

1. 算法原理 1.1 2D坐标与3D坐标的关系 如图&#xff0c;已知世界坐标系上的某点P&#xff08;Xc&#xff0c; Yc&#xff0c; Zc&#xff09;经过相机的内参矩阵可以获得唯一的图像坐标p&#xff08;x&#xff0c; y&#xff09;&#xff0c;但是反过来已知图像上某点p&…

软考初级程序员--学习

1、十进制 转 二进制 1.1、整数十进制87 转换为 二进制为 1010111 1.2 、小数十进制0.125 转为 二进制 为 0.001 使用乘2取整法&#xff0c;一直乘到没有小数 2、二进制 转 十进制 2.1、二进制1010111 转换为 十进制 2.2、 二进制小数0.001 转 十进制 3、循环队列 计算长度通用…

周赛341(模拟、双指针、树上DP)

文章目录周赛341[6376. 一最多的行](https://leetcode.cn/problems/row-with-maximum-ones/)暴力模拟[6350. 找出可整除性得分最大的整数](https://leetcode.cn/problems/find-the-maximum-divisibility-score/)暴力模拟[6375. 构造有效字符串的最少插入数](https://leetcode.c…

JVM系统优化实践(15):GC可视化工具实践

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e; 线上系统的JVM监测要么使用jstat、jmap、jhat等工具查看JVM状态&#xff0c;或者使用监控系统&#xff0c;如Zabbix、Prometheus、Open-FaIcon、Ganglia等。作为…

pyg的NeighborLoader和LinkNeighborLoader

NeighborLoader 1 数据格式要求 需要传入加载的属性值&#xff1a; class NeighborLoader(data: Union[Data, HeteroData, Tuple[FeatureStore, GraphStore]], num_neighbors: Union[List[int], Dict[Tuple[str, str, str], List[int]]], input_nodes: Union[Tensor, None…

进程调度的基本过程

进程调度的基本过程&#x1f50e; 进程是什么&#x1f50e; 进程管理&#x1f50e; 进程中结构体的属性进程标识符(PID)内存指针文件描述符表结构体中与进程调度相关的属性进程的状态进程的优先级进程的上下文进程的记账信息&#x1f50e; 总结&#x1f50e; 结尾&#x1f50e;…

(第十四届蓝桥真题) 整数删除(线段树+二分)

样例输入&#xff1a; 5 3 1 4 2 8 7 样例输出&#xff1a; 17 分析&#xff1a;这道题我想的比较复杂&#xff0c;不过复杂度还是够用的&#xff0c;我是用线段树二分来做的。 我们用线段树维护所有位置的最小值&#xff0c;那么我们每次删除一个数之前先求一遍最小值&a…

停车场管理系统文件录入(C++版)

❤️作者主页&#xff1a;微凉秋意 ✅作者简介&#xff1a;后端领域优质创作者&#x1f3c6;&#xff0c;CSDN内容合伙人&#x1f3c6;&#xff0c;阿里云专家博主&#x1f3c6; 文章目录一、案例需求描述1.1、汽车信息模块1.2、普通用户模块1.3、管理员用户模块二、案例分析三…

mysql:使用终端操作数据库

登录进入终端&#xff1a; mysql -u root -p 展示数据库 SHOW DATABASES; 创建数据库&#xff1a; CREATE DATABASE IF NOT EXISTS RUNOOB_TEST DEFAULT CHARSET utf8 COLLATE utf8_general_ci; 1. 如果数据库不存在则创建&#xff0c;存在则不创建。 2. 创建RUNOOB_TEST数据库…