基于Beanutils.copyProperties()的用法及重写提高效率

目录
  • Beanutils.copyProperties()用法及重写提高效率
    • 一、简介
    • 二、用法
    • 三、重写
      • 原理
  • BeanUtils.copyProperties 使用注意
    • 示例演示
    • 开始进行示例
    • 最后强调

Beanutils.copyProperties()用法及重写提高效率

特别说明本文介绍的是Spring(import org.springframework.beans.BeanUtils)中的BeanUtils.copyProperties(A,B)方法。是将A中的值赋给B。apache(org.apache.commons.beanutils.BeanUtils)中的BeanUtils.copyProperties(A,B)方法是将B中的值赋值给A。

一、简介

BeanUtils提供对Java反射和自省API的包装。其主要目的是利用反射机制对JavaBean的属性进行处理。我们知道,一个JavaBean通常包含了大量的属性,很多情况下,对JavaBean的处理导致大量get/set代码堆积,增加了代码长度和阅读代码的难度。

二、用法

如果你有两个具有很多相同属性的JavaBean,一个很常见的情况就是Struts里的PO对象(持久对象)和对应的ActionForm。例如:一个用户注册页面,有一个User实体类和一个UserActionForm,我们一般会在Action里从ActionForm构造一个PO对象,传统的方式是使用类似下面的语句对属性逐个赋值:

// 获取 ActionForm 表单数据
UserActionForm uForm = (UserActionForm) form;
// 构造一个User对象
User user = new User();
// 逐一赋值
user.setUsername(uForm.getUsername);
user.setPassword(uForm.getPassword);
user.setAge(uForm.getAge);
...........
...........
// 然后调用JDBC、或操作Hibernate 持久化对象User到数据库
HibernateDAO.save(user); 

通过这样的方法如果表单数据N多、100、1000(夸张点。哈哈)、、、、那我们不是要写100、、、1000行set、get了。谁都

不愿意这样做。

而我们使用 BeanUtils.copyProperties() 方法以后,代码量大大的减少,而且整体程序看着也简洁明朗,代码如下:

// 获取 ActionForm 表单数据
UserActionForm uForm = (UserActionForm) form;
// 构造一个User对象
User user = new User();
BeanUtils.copyProperties(uForm,user);
// 然后调用JDBC、或操作Hibernate 持久化对象User到数据库
HibernateDAO.save(user); 

注:如果User和UserActionForm 间存在名称不相同的属性,则BeanUtils不对这些属性进行处理,需要手动处理。例如:

User类里面有个createDate 创建时间字段,而UserActionForm里面无此字段。BeanUtils.copyProperties()不会对此字段做任何处理。必须要自己手动处理。

user.setModifyDate(new Date());  

三、重写

ReflectASM,高性能的反射:

什么是ReflectASM ReflectASM是一个很小的java类库,主要是通过asm生产类来实现java反射,执行速度非常快,看了网上很多和反射的对比,觉得ReflectASM比较神奇,很想知道其原理,下面介绍下如何使用及原理;

public static void main(String[] args) {
        User user = new User();
        //使用reflectasm生产User访问类
        MethodAccess access = MethodAccess.get(User.class);
        //invoke setName方法name值
        access.invoke(user, "setName", "张三");
        //invoke getName方法 获得值
        String name = (String)access.invoke(user, "getName", null);
        System.out.println(name);
    }    

原理

上面代码的确实现反射的功能,代码主要的核心是 MethodAccess.get(User.class);

看了下源码,这段代码主要是通过asm生产一个User的处理类 UserMethodAccess(这个类主要是实现了invoke方法)的ByteCode,然后获得该对象,通过上面的invoke操作user类。

private static Map<Class, MethodAccess> methodMap = new HashMap<Class, MethodAccess>();
    private static Map<String, Integer> methodIndexMap = new HashMap<String, Integer>();
    private static Map<Class, List<String>> fieldMap = new HashMap<Class, List<String>>();
    public static void copyProperties(Object desc, Object orgi) {
        MethodAccess descMethodAccess = methodMap.get(desc.getClass());
        if (descMethodAccess == null) {
            descMethodAccess = cache(desc);
        }
        MethodAccess orgiMethodAccess = methodMap.get(orgi.getClass());
        if (orgiMethodAccess == null) {
            orgiMethodAccess = cache(orgi);
        }  

        List<String> fieldList = fieldMap.get(orgi.getClass());
        for (String field : fieldList) {
            String getKey = orgi.getClass().getName() + "." + "get" + field;
            String setkey = desc.getClass().getName() + "." + "set" + field;
            Integer setIndex = methodIndexMap.get(setkey);
            if (setIndex != null) {
                int getIndex = methodIndexMap.get(getKey);
                // 参数一需要反射的对象
                // 参数二class.getDeclaredMethods 对应方法的index
                // 参数对三象集合
                descMethodAccess.invoke(desc, setIndex.intValue(),
                        orgiMethodAccess.invoke(orgi, getIndex));
            }
        }
    }  

    // 单例模式
    private static MethodAccess cache(Object orgi) {
        synchronized (orgi.getClass()) {
            MethodAccess methodAccess = MethodAccess.get(orgi.getClass());
            Field[] fields = orgi.getClass().getDeclaredFields();
            List<String> fieldList = new ArrayList<String>(fields.length);
            for (Field field : fields) {
                if (Modifier.isPrivate(field.getModifiers())
                        && !Modifier.isStatic(field.getModifiers())) { // 是否是私有的,是否是静态的
                    // 非公共私有变量
                    String fieldName = StringUtils.capitalize(field.getName()); // 获取属性名称
                    int getIndex = methodAccess.getIndex("get" + fieldName); // 获取get方法的下标
                    int setIndex = methodAccess.getIndex("set" + fieldName); // 获取set方法的下标
                    methodIndexMap.put(orgi.getClass().getName() + "." + "get"
                            + fieldName, getIndex); // 将类名get方法名,方法下标注册到map中
                    methodIndexMap.put(orgi.getClass().getName() + "." + "set"
                            + fieldName, setIndex); // 将类名set方法名,方法下标注册到map中
                    fieldList.add(fieldName); // 将属性名称放入集合里
                }
            }
            fieldMap.put(orgi.getClass(), fieldList); // 将类名,属性名称注册到map中
            methodMap.put(orgi.getClass(), methodAccess);
            return methodAccess;
        }
    } 

执行1000000条效率80几毫秒,效率已经没问题了。

BeanUtils.copyProperties 使用注意

首先结论说在前头, BeanUtils.copyProperties 是浅拷贝 。

为什么今天我还想把这个BeanUtils.copyProperties 的使用拿出来军训。

因为我意识到了大家(部分)对深拷浅拷还是不清晰,不知道具体的影响。

示例演示

第一个类:

第二个类:

注意!! 第二个类里面有使用第一个类。

开始进行示例

    public static void main(String[] args) {
        /**
         * 模拟数据 A  complexObject
         */
        ComplexObject complexObjectA=new ComplexObject();
        complexObjectA.setNickName("张一");
        SimpleObject simpleObject=new SimpleObject();
        simpleObject.setName("李四");
        simpleObject.setAge(12);
        complexObjectA.setSimpleObject(simpleObject);
        /**
         * 使用BeanUtils.copyProperties 拷贝 模拟数据 A 生成模拟数据 B
         */
        ComplexObject complexObjectB=new ComplexObject();
        BeanUtils.copyProperties(complexObjectA,complexObjectB);

        System.out.println("拷贝后,查看模拟数据A 和 模拟数据B :");
        System.out.println(complexObjectA.getSimpleObject().toString());
        System.out.println(complexObjectB.getSimpleObject().toString());

        System.out.println("比较模拟数据A 和 模拟数据B 里面的引用对象simple 是否引用地址一样: ");
        System.out.println(complexObjectA.getSimpleObject()==complexObjectB.getSimpleObject()); 

        System.out.println("修改拷贝出来的模拟数据B里面的引用对象simple的属性 age 为 888888");
        complexObjectB.getSimpleObject().setAge(888888);

        System.out.println("修改后,观察原数据A 和拷贝出来的数据 B 里面引用的 对象 simple的属性 age:");
        System.out.println(complexObjectA.getSimpleObject().toString());
        System.out.println(complexObjectB.getSimpleObject().toString());
    }

最后强调

如果你是使用BeanUtils.copyProperties 进行对象的拷贝复制, 一定要注意!

  • 第一点 、你所拷贝的对象内包不包含 其他对象的引用。
  • 第二点、如果包含,那么接下来的方法里无论是操作原对象还是操作拷贝出来的对象是否涉及到 对 对象内 的 那个其他对象的 值的修改 。
  • 第三点、如果涉及到, 修改了,会不会影响到其他方法 对 修改值的使用情况。

就如文中例子, 如果传入过来的一个复杂对象数据A 里面引用了一个 user对象年龄age是10;拷贝出一份数据B后, 操作 数据B的方法把 年龄age改成了88888;

那么后续其他方法用到数据A ,想用的是最初始的 age 为10 ,那么就用不到了,因为浅拷贝的原因受影响,age都变成88888 了。

ok,就提到这。以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们~

时间: 2021-08-31

BeanUtils.copyProperties()参数的赋值顺序说明

目录 BeanUtils.copyProperties()参数的赋值顺序 BeanUtils.copyProperties初体验,及其参数含义解释 用处 案例: 创建一个源类:source 创建一个目标target源类 创建测试类test ignoreProperties参数 案例 案例测试 BeanUtils.copyProperties()参数的赋值顺序 BeanUtils.copyProperties(x,y)有两个不同的jar包,引入不同的包,赋值的顺序不一样. 分别是: org.spr

BeanUtils.copyProperties()拷贝id属性失败的原因及解决

目录 BeanUtils.copyProperties()拷贝id属性失败 部分代码如下 解决方法 BeanUtils.copyProperties 出错 BeanUtils.copyProperties()拷贝id属性失败 po类中id有值,但是使用BeanUtils.copyProperties()拷贝出的vo类id属性为null,检查后发现是因为po继承的父类声明了一个泛型. 部分代码如下 public abstract class AbstractEntity<ID extends Se

解决BeanUtils.copyProperties之大坑

目录 BeanUtils.copyProperties大坑 BeanUtils.copyProperties() 用法及区别 因为两个类引入了两个不同的BeanUtils类 例如 BeanUtils.copyProperties大坑 两个不同的包(springframework , apache)中有一个相同名字的类,相同的方法,方法的作用相同,参数个数相同.就是参数位置不同,是相反的.? import org.springframework.beans.BeanUtils; import or

BeanUtils.copyProperties复制不生效的解决

目录 前言 问题的排查 问题的扩展 前言 呵呵 前端时间使用 BeanUtils.copyProperties 的时候碰到了一个这样的问题 我有两个实体, 有同样的属性, 一个有给定的属性的 getter, 另外一个有 给定的属性的 setter, 但是 我使用 BeanUtils.copyProperties 的时候 把来源对象的这个属性 复制不到 目标对象上面 然后 当时也跟踪了一下代码, 然后 这里整理一下 改代码片段吧 然后在调试的过程中 也发现了一些其他的问题, 呵呵 算是额外的了解吧

Java BeanUtils.copyProperties的详解

场景 开发中经常遇到,把父类的属性拷贝到子类中.通常有2种方法: 1.一个一个set 2.用BeanUtils.copyProperties 很显然BeanUtils更加方便,也美观很多. 那么任何情况都能使用BeanUtils么,当然不是.要先了解他. BeanUtils是深拷贝,还是浅拷贝? 是浅拷贝. 浅拷贝: 只是调用子对象的set方法,并没有将所有属性拷贝.(也就是说,引用的一个内存地址) 深拷贝: 将子对象的属性也拷贝过去. 什么情况适合用BeanUtils 如果都是单一的属性,那么

BeanUtils.copyProperties复制属性失败的原因及解决方案

目录 BeanUtils.copyProperties复制属性失败 描述 解决办法 BeanUtils.copyProperties应用的改进 为解决这个问题我重写了部分spring BeanUtils的代码 BeanUtils.copyProperties复制属性失败 描述 在JavaE中使用 BeanUtils.copyProperties,把A对象的name.age等属性复制到B对象中,A与B对象的类型不同.出现的问题是复制属性失败,根本原因是 BeanUtils找不到set.get方法.

聊聊BeanUtils.copyProperties和clone()方法的区别

目录 首先,BeanUtils有两种: 效率: 需要在pom文件中引入这个包 在pom文件里面引入所需要的包 新建一个实体类StudentEntity实现Cloneable接口 测试方法 最近撸代码的时候发现有人将一个对象的值赋给另一个对象的时候,并没有使用常规的set/get方法去给对象赋值,而是采用BeanUtils.copyProperties(A,B)这个方法去赋值,但是有的还是有局限性,比如Date类型的值无法赋值,只能赋值为null,所以我大致百度了一下,作为记录. 首先,BeanU

BeanUtils.copyProperties使用总结以及注意事项说明

目录 1.前言 2.一般使用 3.拷贝属性时忽略空值 4.使用注意事项(1) 5.使用注意事项(2) 6.使用注意事项(3) 1.前言 开发过程中,讲一个对象的属性和值赋值到另一个对象上,大量使用了get.set方法,看着很臃肿,思考下肯定不只有我有这种想法,所以技术上肯定有方法能解决这个问题,所以查阅了一些资料发现了BeanUtils.copyProperties这个方法以下是这次所有的总结以及使用时的注意事项. 使用org.springframework.beans.BeanUtils.co

java Beanutils.copyProperties( )用法详解

这是一篇开发自辩甩锅稿~~~~ 昨天测试小姐姐将我的一个bug单重开了,emmmm....内心OS:就调整下对象某个属性类型这么简单的操作,我怎么可能会出错呢,一定不是我的锅!!but再怎么抗拒,bug还是要改的,毕竟晚上就要发版本了~~ 老老实实将我前天改的部分跟了一遍,恩,完美,没有任何的缺失~~but本应success的测试数据,接口返还的结果确实是false来着,那还是老老实实debug吧. 一步步跟下来,恩,多么顺畅,就说一定不是我的锅~~诶?不对不对,这里的ID值,为啥是null?传

如何使用BeanUtils.copyProperties进行对象之间的属性赋值

1.使用org.springframework.beans.BeanUtils.copyProperties方法进行对象之间属性的赋值,避免通过get.set方法一个一个属性的赋值 /** * 对象属性拷贝 <br> * 将源对象的属性拷贝到目标对象 * * @param source 源对象 * @param target 目标对象 */ public static void copyProperties(Object source, Object target) { try { BeanU

JSP 开发之Spring BeanUtils组件使用

JSP 开发之Spring BeanUtils组件使用 用于演示的javabean import java.util.Date; public class People { private String name; private int age; private Date birth; public People(String name, int age, Date birth) { super(); this.name = name; this.age = age; this.birth =

Commons beanutils组件简介

Commons Beanutils是Apache开源组织提供的用于操作JAVA BEAN的工具包.使用commons beanutils,我们可以很方便的对bean对象的属性进行操作.今天为大家介绍一下该包的常用方法. 1.什么是BeanUtils 程序中对javabean的操作很频繁, 所以apache提供了一套开源的api,方便对javabean的操作,即BeanUtils组件. 2.BeanUtils的作用 简化javabean的操作. 在一般的写bean组件的时候,都必须要写setter

Java类库BeanUtils组件使用方法及实例详解

BeanUtils BeanUtils是Apache commens组件里面的成员,由Apache提供的一套开源api,用于简化对javaBean的操作,能够对基本类型自动转换. JavaBean BeanUtils组件是用于简化javaBean的操作,那么什么是javaBean呢?简单来说,javaBean实质就是java类,只不过是遵循了某种规范的java类. javaBean的特点: 必须具有一个无参的构造方法 属性必须私有化 私有化的属性必须通过public类型的方法来暴露,也就是说要出

Java工具类BeanUtils库介绍及实例详解

BeanUtils工具由Apache软件基金组织编写,提供给我们使用,主要解决的问题是:把对象的属性数据封装到对象中. 在整个J2EE的编程过程中,我们经常会从各种配置文件中读取相应的数据,需要明白的一点是从配置文件中读取到的数据都是String,但是很显然我们的应用程序中不仅仅有String一种数据类型,比如:基本数据类型(int.double.char.float等),还有自定义数据类型(引用数据类型), 那么我们必须面临的一个问题就是讲字符串类型转换为各种具体的数据类型,该怎么办呢? 有两

为什么不推荐使用BeanUtils属性转换工具示例详解

什么是BeanUtils工具 BeanUtils工具是一种方便我们对JavaBean进行操作的工具,是Apache组织下的产品. BeanUtils工具一般可以方便javaBean的哪些操作? 1)beanUtils 可以便于对javaBean的属性进行赋值. 2)beanUtils 可以便于对javaBean的对象进行赋值. 3)beanUtils可以将一个MAP集合的数据拷贝到一个javabean对象中. 1 背景 之前在专栏中讲过"不推荐使用属性拷贝工具",推荐直接定义转换类和方

SpringBoot+Dubbo+Seata分布式事务实战详解

前言 Seata 是 阿里巴巴开源的分布式事务中间件,以高效并且对业务0侵入的方式,解决微服务场景下面临的分布式事务问题. 事实上,官方在GitHub已经给出了多种环境下的Seata应用示例项目,地址:https://github.com/seata/seata-samples. 为什么笔者要重新写一遍呢,主要原因有两点: 官网代码示例中,依赖太多,分不清哪些有什么作用 Seata相关资料较少,笔者在搭建的过程中,遇到了一些坑,记录一下 一.环境准备 本文涉及软件环境如下: SpringBoot

java实现微信点餐申请微信退款

应用场景: 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家账号上. 注意事项: 1.交易时间超过一年的订单无法提交退款 2.微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单好和设置不同的退款单号.申请退款总金额不能超过订单金额.一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号. 3.请求频率限制,150qps,即每秒钟正常的申请退

JavaWeb实现用户登录注册功能实例代码(基于Servlet+JSP+JavaBean模式)

下面通过通过图文并茂的方式给大家介绍JavaWeb实现用户登录注册功能实例代码,一起看看吧. 一.Servlet+JSP+JavaBean开发模式(MVC)介绍 Servlet+JSP+JavaBean模式(MVC)适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp负责数据显示,javabean负责封装数据. Servlet+JSP+JavaBean模式程序各个模块之间层次清晰,web开发推荐采用此种模式. 这里以一个最常用的用户登录注册程序来讲解Servlet+JS