利用cglib的BeanCopier用原型模式以及享元模式完成对象的拷贝

2023-11-17

实际上对象拷贝的工具有很多种,比如apache BeanUtils、apache PropertyUtils、spring BeanUtils。在一些业务代码中现在经常看到的都是spring BeanUtils来进行对象拷贝。大部分情况下来说已经足够了,但如果居于性能考虑,以上几种工具都是利用反射的原理来完成的,性能相比cglib beanCopier利用动态代理实现稍差一筹,这里不去对比几种工具的性能,只展示BeanCopier如何进行使用。

一、非cglib下的对象拷贝方式

1、原型模式就是从一个已存在的对象创建出另一个对象

在实际业务开发中我们经常需要完成对象从DO到DTO或者VO的拷贝,大部分情况下,DTO/VO的属性都会与DO中的属性保持一致,如果每次都使用set方法去设置值就太麻烦了。需要通过一个通用的方法完成对象拷贝,如可以覆盖Object类的clone方法完成克隆操作

package com.zcj.javabase.designpattern.prototype;

/**
 * 学生DO
 * @author zcj
 * @date 2021/3/24 06:16
 */
public class StudentDO {

    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

2、覆盖父类clone的局限性

使用覆盖父类的clone返回的是一个Object对象,使用时可强转一个新的StudentDO对象,但我们常常需要做的是从StudentDO拷贝一个StudentVO出来。此时就需要我们自己实现一个新的对象拷贝的方法,可传入一个类型参数,返回一个对应类型的对象出去。

public <T> T clone(Class<T> clazz) {
    T clazzInstance = null;
    try {
        clazzInstance = clazz.newInstance();
        Method setId = clazz.getMethod("setId", Long.class);
        setId.invoke(clazzInstance, this.id);

        Method setName = clazz.getMethod("setName", String.class);
        setName.invoke(clazzInstance, this.name);
    } catch (Exception e) {
        e.printStackTrace();
    } 

    return clazzInstance;
}

这样的做法是不通用,并且属性非常多的时候,就会特别的麻烦,可以使用BeanUtils.copyProperties替换掉原来的一大堆getMethod和invoke,如:

public <T> T clone(Class<T> clazz) {
    T clazzInstance = null;
    try {
        clazzInstance = clazz.newInstance();
        BeanUtils.copyProperties(this, clazzInstance);
    } catch (Exception e) {
        e.printStackTrace();
    }

    return clazzInstance;
}

二、cglib的BeanCopier的的方式实现对象拷贝

1、引入对象的依赖(正常项目中spring会有cglib的包,可直接使用spring的BeanCopier类,而不用单独的引入以下依赖)

<dependency>
    <groupId>asm</groupId>
    <artifactId>asm-all</artifactId>
    <version>3.3.1</version>
</dependency>

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>3.3.0</version>
</dependency>

2、增加工具类(工具类中实现了享元模式)

享元模式解决的是当我们需要多次使用某个对象时,只需要创建一次,然后缓存起来,后续再使用的时候直接获取。而不用每次使用都进行创建,可通过Map进行缓存起来。这里的工具类就是使用Map把生成的BeanCopier缓存起来

package com.zcj.javabase.designpattern.flyweight;

import org.springframework.cglib.beans.BeanCopier;
import java.util.HashMap;
import java.util.Map;

/**
 * 对象拷贝工具类
 * @author zcj
 * @since 2021/2/24
 */
public class BeanCopierUtils {

    /**
     * 缓存beanCopier对象
     */
    private static final Map<String, BeanCopier> BEAN_COPIER_MAP = new HashMap<>();

    /**
     * 属性拷贝
     * @param source 源对象
     * @param target 目标对象
     */
    public static void copyProperties(Object source, Object target) {
        String beanKey = generateKey(source.getClass(), target.getClass());
        BeanCopier copier = BEAN_COPIER_MAP.get(beanKey);
        if (copier == null) {
            synchronized (BeanCopierUtils.class) {
                if (!BEAN_COPIER_MAP.containsKey(beanKey)) {
                    copier = BeanCopier.create(source.getClass(), target.getClass(), false);
                    BEAN_COPIER_MAP.put(beanKey, copier);
                }  else {
                    copier = BEAN_COPIER_MAP.get(beanKey);
                }
            }
        }
        copier.copy(source, target, null);
    }

    private static String generateKey(Class<?> sourceClass, Class<?> targetClass) {
        return sourceClass.toString() + targetClass.toString();
    }
}  

3、修改原来的clone方法

public <T> T clone(Class<T> clazz) {
    T clazzInstance = null;
    try {
        clazzInstance = clazz.newInstance();
        BeanCopierUtils.copyProperties(this, clazzInstance);
    } catch (Exception e) {
        e.printStackTrace();
    }

    return clazzInstance;
}

4、测试

package com.zcj.javabase.designpattern.prototype;

/**
 * 学生VO
 * @author zcj
 * @date 2021/3/24 06:16
 */
public class StudentVO {

    private Long id;

    private String name;

    private String address;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}
    public static void main(String[] args) {

        StudentDO studentDO = new StudentDO();
        studentDO.setId(100L);
        studentDO.setName("zcj");

        StudentVO clone = studentDO.clone(StudentVO.class);
        System.out.println(JSONObject.toJSONString(clone));
    }

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

利用cglib的BeanCopier用原型模式以及享元模式完成对象的拷贝 的相关文章

  • 使用 JPA/ORM 生成数据库模式是一个坏主意吗?

    Salve Part of 另一个问题 答案 https stackoverflow com questions 7595578关于 SO 以及其他声称相同的声明 如果您通过 JPA 更新数据库架构 但通常不是一个好的做法 您是否真的不应该
  • 在 Java 中跨平台地播种随机生成器,无需时间

    我几乎同时在两个线程上初始化两个随机数生成器 并且我希望这两个生成器的行为完全不同 我会打电话Random nextInt 7 经常一个接一个地在两台发电机上运行 使用System currentTimeMillis 这不是一个好主意 因为
  • 是否可以使用检测重新定义核心 JDK 类?

    我想重新定义字节码StackOverflowError构造函数 因此当堆栈溢出发生时我有一个 钩子 我想要做的就是在构造函数的开头插入对我选择的静态方法的单个方法调用 是否有可能做到这一点 您应该能够使用两种方法之一来完成此操作 除非在过去
  • Java 的 System.arraycopy() 对于小数组有效吗?

    是Java的System arraycopy 对于小数组来说是高效的 或者它是本机方法这一事实是否使其可能比简单的循环和函数调用效率低得多 本机方法是否会因跨越某种 Java 系统桥梁而产生额外的性能开销 稍微扩展一下 Sid 所写的内容
  • 在 Eclipse 中跨文件搜索注释掉的代码

    有没有一种快速方法可以在 Eclipse 中查找 Java 文件中所有注释掉的代码 也许是搜索中的任何选项 或者任何可以执行此操作的附加组件 它应该只能找到被注释掉的代码 而不是普通的注释 在 Eclipse 中 我只是在打开正则表达式复选
  • 使用 Hibernate 或 Spring 打印 DBMS_OUTPUT.put_line

    我想知道 Hibernate 或 Spring 或任何第 3 方库是否提供将 DBMS OUTPUT put line 消息直接打印到 system out 或日志文件的能力 目的是在控制台中同时显示 PLSQL 日志消息和 java 日志
  • 如何在 Java 中根据 XSD 1.1 验证 XML?

    在 Java 中根据 XML Schema 1 1 验证 XML 文件的最佳方法是什么 我从中获取了代码tutorial http www ibm com developerworks xml library x javaxmlvalida
  • Android - 内容值覆盖现有行

    我正在尝试使用插入值ContentValues 我已将 5 个值插入到 5 列中 运行应用程序后 我只有最后一组值的行ContentValues 前四组未插入 ContentValues cv new ContentValues cv pu
  • 使用 Java 检索 Window 进程的 CPU 使用率

    我正在寻找一个 Java 解决方案来查找 Windows 中正在运行的进程的 CPU 使用情况 查了一下网上 关于Java解决方案的信息似乎很少 请记住 我并不是要查找 JVM 的 CPU 使用情况 而是要查找当时在 Windows 中运行
  • UnsupportedOperationException:特权进程中不允许使用 WebView

    我在用android sharedUserId android uid system 在我的清单中获得一些不可避免的权利 从 HDMI 输入读取安卓盒子 http eweat manufacturer globalsources com s
  • 将位于 jar 中的文件读取为 java.io.File 对象

    与此类似的问题已发布 但似乎没有一个答案对我的情况有帮助 我正在编写一个程序包 它使用 Google 的凭据来获取 Google Apps 用户 为此 我使用服务帐户 因此为了检索凭据 我需要提供 除其他外 一个 p12 签名文件 Cred
  • java“类文件包含错误的类”错误

    我正在尝试制作一个控制台应用程序来测试我的网络服务 我成功部署了一个网络服务http localhost 8080 WS myWS http localhost 8080 WS myWS我用 wsimport 制作了代理类 wsimport
  • JavaPreparedStatementUTF-8字符问题

    我有一份准备好的声明 PreparedStatement st 在我的代码中 我尝试使用 st setString 方法 st setString 1 userName userName 的值为 ak a setString 方法将 ak
  • 如何加快 jar 签名者的速度?

    我使用 ant 来签署我的 jars 以进行网络启动部署 Ant signjar 在 Web 启动签名时非常慢 如何加快签名过程 我找到了一种可能的解决方案 早些时候 在构建脚本 ant signjar 中 按顺序调用所有 jar 我们使用
  • 当另一个线程发生事情时从主线程获取数据?

    目前我有一个线程正在运行一个侦听连接的套接字 当它收到连接时 它需要上传在主线程中收集的数据 即从主线程获取数据 但是 我传递了对象的实例 但它从未使用等待连接时收集的数据进行更新 有没有正确的方法来做到这一点 我用谷歌搜索了一下 似乎找不
  • 在同一台计算机上设置 JBoss 的多个实例

    我在 JBoss 社区中找到了下一页 http www jboss org community wiki ConfigurePorts http www jboss org community wiki ConfigurePorts 有下一
  • 在Java程序中计算zip文件的md5哈希值

    我有一个 zip 文件 在我的 Java 代码中我想计算 zip 文件的 md5 哈希值 有没有我可以用于此目的的 java 库 一些例子将非常感激 谢谢 几周前我通过这篇文章做到了这一点 http www javalobby org ja
  • servlet 如何获取 servlet 之外的文件的绝对路径?

    我们一直在使用 System getProperties user dir 来获取属性文件的位置 现在它已经部署在 Tomcat 上 通过 servlet 系统调用将位置指定为 tomcat 而不是属性文件所在的位置 我们如何动态调用属性文
  • 从 AJP 连接器请求中检索 Shibboleth 属性

    当我在 Apache 上运行 Shibboleth 身份验证时遇到了一个奇怪的问题 当 Tomcat7 在后端运行时 Apache 通过 mod proxy ajp 发送所有内容 Shibboleth 的参数也是如此 In the 文档 h
  • 错误:运算符不存在:整数 = 字符变化,使用 Postgres 8.2

    我有一个用旧版本的 Eclipse Ganymede 如果我没记错的话 开发的 Java EE Web 应用程序 我最近迁移到 Kubuntu 12 04 LTS 并将应用程序迁移到 Eclipse Kepler 我从 Eclipse 网站

随机推荐

  • SQL语句的MINUS,INTERSECT和UNION ALL

    SQL语句中的三个关键字 MINUS 减去 INTERSECT 交集 和UNION ALL 并集 关于集合的概念 中学都应该学过 就不多说了 这三个关键字主要是对数据库的查询结果进行操作 正如其中文含义一样 两个查询 MINUS是从第一个查
  • vue实现下载文件和图片功能

    vue实现图片或文件下载功能 今天一个需求就是实现图片下载功能 刚开始以为很简单没有什么逻辑可写 就以为调用后端接口就可以了 调用之后发现有问 题 看来还是没有想象的那么简单 1 要自己创建一个a标签 以下就是下载功能的实现 这里是调用接口
  • 2.4 HTTP请求方法

    在客户端向服务器端发送请求时 需要确定使用的请求方法 请求方法表明对URL指定资源的操作方式 服务器会根据不同的请求方法进行不同的响应 在HTTP 1 1中 共定义了8种请求方法 具体如下 GET 请求指定的内容并返回 POST 向指定资源
  • 分布式缓存的切片模式-hash一致性切片

    文章目录 一 为什么使用缓存 二 为什么使用分布式 三 使用什么模式 四 常规切片模式的弊端 五 更加犀利的切片模式 hash一致性切片 六 不完美的数据倾斜以及解决方案 6 1 数据倾斜 6 2 解决办法 一 为什么使用缓存 当前 我们通
  • 基于stm32的keil调试

    目录 基于stm32的keil调试 前言 实验目的 问题 debug 定位问题 解决 总结 基于stm32的keil调试 本文目标 基于stm32的keil调试 按照本文的描述 应该可以跑通实验并举一反三 先决条件 装有编译和集成的开发环境
  • 06_个人注释版本(01版本)GTK播放器__基于Linux系统下的mplayer播放器

    include
  • 计算机视觉结合深度学习项目-智能停车场空车位实时识别

    欢迎来到本博客 本次博客内容将继续讲解关于OpenCV的相关知识 作者简介 目前计算机研究生在读 主要研究方向是人工智能和群智能算法方向 目前熟悉python网页爬虫 机器学习 计算机视觉 OpenCV 群智能算法 然后正在学习深度学习的相
  • Android框架源码解析之(五)Retrofit

    源码地址 https github com square retrofit Retrofit源码结构 可以看出Retrofit是使用idea maven依赖编写的Java工程 并不是一个Android 工程 Retrofit的简单使用 1
  • 学习docker之路(三)

    目录 一 docker容器内操作 二 网络 一 docker容器内操作 1 将容器导出为归档 docker export 容器名称 o 归档包名称 root localhost docker export 05 o mynginx v2 t
  • leetcode_165. Compare Version Numbers 比较版本大小

    题目 Compare two version numbers version1 and version2 If version1 gt version2 return 1 if version1 lt version2 return 1 o
  • git简单使用与安装(小白01)(还看不懂我下个版本给图片)

    一 1 下载git 在官网上下载 下载成功后 闭着眼睛狂点下一步 然后就成功了 嘿嘿 二 1 随便创建一个新的文件夹 打开文件夹点这里 下图 然后输入cmd 然后在按回车 cmd中输入 git init 生成git文件 该文件是隐藏状态 2
  • redis06_ redis的订阅发布模式(redis做MQ中间件)、持久化(rdb,aof)、事务

    一 redis订阅发布模式 1 1 简介 redis 可以做消息中间件 MQ message queue 通常通过订阅发布模式来实现 消息订阅发布模式 还可以基本数据类型Lists实现 点到点模式 可以使用lpush lpop 实现消息先进
  • linux中I/O流中的全缓冲、行缓冲和无缓冲,简明实例演示

    说到缓冲 缓存之类的术语 通常都会和执行效率联系到一起 在标准I O库中提供缓冲的主要目的就是减少系统函数read和write的调用 从而能够减少系统CPU时间 标准I O库的缓冲主要分为3种 全缓冲 行缓冲和不缓冲 笔者就3种缓冲写了一些
  • 时钟电路设计概述 - 数字电路设计

    时钟电路设计概述 数字电路设计 2010 09 10 阅 转 分享 本文一般性地讲解了数字电路设计中的 时钟电路设计 包括有源 晶振
  • Unity3D教程笔记——unity初始02

    史上最全Unity3D教程 哔哩哔哩 bilibilihttps www bilibili com video BV12s411g7gU p 11 vd source f38a8a7e90133354051c463eb03a3b4f 这里是
  • 键盘输入流的缓冲效应 I/O流 缓冲I/O

    引子 有时候 你会发现 你的程序老是读不进某个值 但是反反复复看代码 又感觉自己代码真的没有问题 比如说下面这个代码 include stdio h int main int num char ch scanf d num scanf c
  • 【08】频率响应_详细数学推导G(jw)_滤波器

    本部分为B站up主DR CAN系列课程动态系统的建模与分析笔记 已获得up主授权 课程视频链接为 动态系统的建模与分析 8 频率响应 详细数学推导 G jw 滤波器 哔哩哔哩 bilibili
  • Selenium中get_attribute、get_property和text详解

    Selenium中WebElement类中get attrubute get propertry和text详解 get attribute get property和text的详解 特点如下 get property self name g
  • 2D/3D人体姿态估计 (2D/3D Human Pose Estimation)

    1 基本概念 算法改进入口 网络设计 特征流 损失函数 数据集的重要性 只要有一个好的 针对性的数据集 问题都可以解决 过集成新一代AutoML技术 可降低算法试错成本 人体姿态估计 Human Pose Estimation 是指图像或视
  • 利用cglib的BeanCopier用原型模式以及享元模式完成对象的拷贝

    实际上对象拷贝的工具有很多种 比如apache BeanUtils apache PropertyUtils spring BeanUtils 在一些业务代码中现在经常看到的都是spring BeanUtils来进行对象拷贝 大部分情况下来