Java常用的几种属性拷贝工具类使用总结

怕什么真理无穷,进一步有近一步的欢喜文章目录开头聊几句Java属性拷贝工具类使用总结字段和属性使用说明**org.springframework.beans.BeanUtils#copyProperties**org.apache.commons.beanutils.PropertyUtils#_copyProperties_org.apache.commons.beanutils.BeanUtils#_copyProperties原理探索Spring#BeanUtilsapache.commons#.

大家好,又见面了,我是全栈君。

怕什么真理无穷,进一步有近一步的欢喜

开头聊几句

  • 1、这周不是很忙,工作上事情不是很多,有空就看看一些技术点
  • 2、网上很多的技术文章和资料是有问题的,要学会辨证的看待,不能随便就拿来用,起码要自己验证一下
  • 3、关注当下,关注此刻,如果你真正阅读本篇文章,请花几分钟时间的注意力阅读,相信你会有收获的

Java属性拷贝工具类使用总结

对项目中经常使用的属性拷贝工具类进行总结:

  • org.apache.commons.beanutils.BeanUtils
  • org.apache.commons.beanutils.PropertyUtils
  • org.springframework.beans.BeanUtils

字段和属性

首先明确下在Java中字段和属性的区别。

属性是不是类里最上边的那些全局变量吗?比如:

public class UserTest{ 
   
    private String userName;
    private String password;


    public String getUserName() { 
   
        return userName;
    }

    public void setUserName(String userName) { 
   
        this.userName = userName;
    }

    public String getPassword() { 
   
        return password;
    }

    public void setPassword(String password) { 
   
        this.password = password;
    }

    public String getHello() { 
   
        return "hello";
    }

    public void setHello(String str) { 
   
    }
}

上面 private String userName;private String password;。准确的来说它们应该称为:字段,而不是本次要讲的属性。

下面简述一下:什么是Java中的属性

Java中的属性(property),通常可以理解为get和set方法,而字段(field),通常叫做“类成员”,或“类成员变量”,有时也叫“域”,理解为“数据成员”,用来承载数据的。

直白点就是Java中的属性是指:设置和读取字段的方法,也就是平常见到的set和get方法。只要是set和get开头的方法在Java里都认为它是属性(请注意这句话,等下后边会写代码做验证)

属性名称:就是set和get方法名 去掉”set”和”get”后的内容

比如:

public void setUserName(String userName) { 
   
	this.userName = userName;
}

它的属性名称是:userName(也就是方法名称”setUserName”去掉“set”)

当然 setUserName和 getUserName 方法是指同一个属性 UserName,

这里再次提醒:字段和属性不是同一个东西。

代码验证属性
上面代码中还有一个 getHellosetHello , JDK 中有个API Introspector

在这里插入图片描述

在这里插入图片描述

获取的是java.beans.BeanInfo 类。这个类可以通过

java.beans.BeanInfo#getPropertyDescriptors : 获取java bean 所有的属性。

public static void main(String[] args) throws IntrospectionException { 
   
    BeanInfo beanInfo = Introspector.getBeanInfo(UserTest.class);
    // 得到类中的所有的属性描述器
    PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
    System.out.println("属性的个数:" + pds.length);
    for (PropertyDescriptor pd : pds) { 
   
        System.out.println("属性:" + pd.getName());
    }
}

结果:

属性的个数:4
属性:class
属性:hello
属性:password
属性:userName

上面多了一个 class ,原因很简单,因为Object类是所有类的父类,Object类里有个方法叫 getClass();
所以这也验证了咱们刚才说的: “只要是set或者get开头的方法都叫属性”

使用说明

default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
public : 对所有类可见。使用对象:类、接口、变量、方法
private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

org.springframework.beans.BeanUtils#copyProperties

1.基本类型和包装类型会自动转换, 方法名称相同,返回值类型和参数类型不同,不进行复制,也不报错_

2.支持指定忽略某些属性不复制

3、支持类的修饰符 default 、 public

org.apache.commons.beanutils.PropertyUtils#copyProperties

1.基本类型和包装类型会自动转换

2.方法名称相同,返回值类型和参数类型不同,复制失败,会报错,如下:

_argument type mismatch – had objects of type “java.lang.Double” but expected signature “java.lang.String”

3.只支持类的修饰符 public,如果是default 则直接不会进行转换(注意内部类复制也要加public)

org.apache.commons.beanutils.BeanUtils#_copyProperties

1.基本类型和包装类型会自动转换

2.方法名称相同,返回值类型和_ _参数类型不同,不复制,不报错

3.只支持类的修饰符 public,如果是default 则直接不会进行转换(注意内部类复制也要加public)

tips: Spring和apache的_copyProperties_属性的方法源和目的参数的位置正好相反,所以导包和调用的时候都要注意一下。

// Apache
public static void 
  copyProperties(final Object dest, final Object orig)
    
// Spring
public static void
  copyProperties(Object source, Object target)

性能参考:
Bean复制的几种框架性能比较(Apache BeanUtils、PropertyUtils,Spring BeanUtils,Cglib BeanCopier)

在这里插入图片描述

摘要总结:Spring是在次数增多的情况下,性能较好,在数据较少的时候,性能比PropertyUtils的性能差一些。PropertyUtils的性能相对稳定,表现是呈现线性增长的趋势。而Apache的BeanUtil的性能最差,无论是单次Copy还是大数量的多次Copy性能都不是很好。

使用的压测工具备忘:Java使用JMH进行简单的基准测试Benchmark : http://irfen.me/java-jmh-simple-microbenchmark/

根据上面的具体的分析还是使用 :org.springframework.beans.BeanUtils#copyProperties
原因

1.这个方法在复制的时候不会因为属性的不同而报错,影响代码执行

2.性能方面也相对较好

其他Apache的两个,
1、org.apache.commons.beanutils.PropertyUtils#copyProperties 复制会直接报错

2、org.apache.commons.beanutils.BeanUtils#copyProperties 性能相对较差

原理探索

核心本质都是使用反射实现。具体的实现代码稍有不同。

Spring#BeanUtils

private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
                                   @Nullable String... ignoreProperties) throws BeansException { 
   

    Assert.notNull(source, "Source must not be null");
    Assert.notNull(target, "Target must not be null");

    Class<?> actualEditable = target.getClass();
    if (editable != null) { 
   
        if (!editable.isInstance(target)) { 
   
            throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
                                               "] not assignable to Editable class [" + editable.getName() + "]");
        }
        actualEditable = editable;
    }
    PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
    List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

    for (PropertyDescriptor targetPd : targetPds) { 
   
        Method writeMethod = targetPd.getWriteMethod();
        if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) { 
   
            PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
            if (sourcePd != null) { 
   
                Method readMethod = sourcePd.getReadMethod();
                if (readMethod != null &&
                    ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) { 
   
                    try { 
   
                        if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { 
   
                            readMethod.setAccessible(true);
                        }
                        Object value = readMethod.invoke(source);
                        if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { 
   
                            writeMethod.setAccessible(true);
                        }
                        writeMethod.invoke(target, value);
                    }
                    catch (Throwable ex) { 
   
                        throw new FatalBeanException(
                            "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                    }
                }
            }
        }
    }
}

1、获取 目标对象 所有的属性 targetPds

PropertyDescriptor_[] _targetPds = getPropertyDescriptors(actualEditable);

2、循环 targetPds ,并在源对象取出对应的属性

PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName_())_;

3、r如果不是修饰不是public,**暴力反射 ,**然后使用对属性进行设值

setAccessible_(true);// 暴力反射
writeMethod.invoke
(target, value)_;

### apache.commons#BeanUtils

  • org.apache.commons.beanutils.BeanUtilsBean#copyProperties

简单截取核心代码:

// org.apache.commons.beanutils.BeanUtilsBean#copyProperties

final PropertyDescriptor[] origDescriptors =
    getPropertyUtils().getPropertyDescriptors(orig);
for (PropertyDescriptor origDescriptor : origDescriptors) { 
   
    final String name = origDescriptor.getName();
    if ("class".equals(name)) { 
   
        continue; // No point in trying to set an object's class
    }
    if (getPropertyUtils().isReadable(orig, name) &&
        getPropertyUtils().isWriteable(dest, name)) { 
   
        try { 
   
            final Object value =
                getPropertyUtils().getSimpleProperty(orig, name);
            copyProperty(dest, name, value);
        } catch (final NoSuchMethodException e) { 
   
            // Should not happen
        }
    }
}
// org.apache.commons.beanutils.BeanUtilsBean#copyProperty
getPropertyUtils().setSimpleProperty(target, propName, value);

// org.apache.commons.beanutils.PropertyUtilsBean#setSimpleProperty
 invokeMethod(writeMethod, bean, values);

1、 获取的是源对象的所有的属性

final PropertyDescriptor[] origDescriptors = getPropertyDescriptors(orig);

2、如果属性是class,不复制

if (“class”.equals_(name)) { continue; // No point in trying to set an object’s class}_

3、循环源对象的属性,做一些检验

copyProperty(dest, name, value);
1、会检验目标对象是否有源对象的属性,没有跳过
2、获取属性的名称类型

4、然后给目标对象设置,最终还是使用反射

method.invoke_(bean, values)_;

apache.commons#PropertyUtils

  • org.apache.commons.beanutils.PropertyUtilsBean#copyProperties

简单截取核心代码:

// org.apache.commons.beanutils.PropertyUtilsBean#copyProperties
final PropertyDescriptor[] origDescriptors =
    getPropertyDescriptors(orig);
for (PropertyDescriptor origDescriptor : origDescriptors) { 
   
    final String name = origDescriptor.getName();
    if (isReadable(orig, name) && isWriteable(dest, name)) { 
   
        try { 
   
            final Object value = getSimpleProperty(orig, name);
            if (dest instanceof DynaBean) { 
   
                ((DynaBean) dest).set(name, value);
            } else { 
   
                setSimpleProperty(dest, name, value);
            }
        } catch (final NoSuchMethodException e) { 
   
            if (log.isDebugEnabled()) { 
   
                log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
            }
        }
    }
}
// org.apache.commons.beanutils.PropertyUtilsBean#invokeMethod
method.invoke(bean, values);

1、 获取的是源对象的所有的属性

final PropertyDescriptor[] origDescriptors = getPropertyDescriptors(orig);

2、循环源对象的属性,然后给目标对象设置,最终还是使用反射

总结

结合使用说明以及相关的性能和原理分析,建议使用 org.springframework.beans.BeanUtils#copyPropertie

参考资料

https://www.cnblogs.com/kaka/archive/2013/03/06/2945514.html



Java编程技术乐园:分享干货技术,每天进步一点点,小的积累,带来大的改变!


扫描关注,后台回复【秘籍】,获取珍藏干货! 99.9%的伙伴都很喜欢

关注不迷路| center| 747x519

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/120966.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • java switch用法_Java switch语句

    java switch用法_Java switch语句Javaswitch语句用于从多个条件执行一个语句。它就像if-else-if语句一样。语法:switch(expression){casevalue1://codetobeexecuted;break;//optionalcasevalue2://codetobeexecuted;break;//optional……default://codetobee…

    2022年7月8日
    29
  • SELinux深入理解

    SELinux深入理解

    2021年10月27日
    49
  • 获取不同长度的UUID[通俗易懂]

    获取不同长度的UUID[通俗易懂]在公司,有时候让处理一些命名规则时,要使用一个唯一标识,还是十六进制的,需要多少位看领导心情.怎么做呢?你别说用随机产生组合的方法啊?虽然那个可行,但是我觉得难受.知道有UUID这个玩意儿之后,那就好办了.UUID含义是通用唯一识别码(UniversallyUniqueIdentifier),这是一个软件建构的标准,也是被开源软件基金会(OpenSoftwareFoundatio

    2022年8月10日
    11
  • 2023考研高数接力题典1800习题讲解

    2023考研高数接力题典1800习题讲解第一部分(函数、极限、连续)极限求法:①直接代入数值②约去不能代入的零因子③分子分母同除最高次幂④分子分母有理化⑤公式法⑥等价无穷小量的代换⑦洛必达法则⑧换底公式(对数)入门练习填空题讲解(1~4):第一题:我们通过观察,发现是0/0型的,自然想到了洛必达法则。百度百科:洛必达法则是在一定条件下通过分子分母分别求导再求极限来确定未定式值的方法。众所周知,两个无穷小之比或两个无穷大之比的极限可能存在,也可能不存在。因此,求这类极限时往往需要适当的变形,转化成可利用极限运算法则或重要

    2022年8月11日
    6
  • java fgc_记一次频繁FGC的简单排查

    java fgc_记一次频繁FGC的简单排查简书占小狼转载请注明原创出处,谢谢!如果读完觉得有收获的话,欢迎点赞加关注周末愉快,今天有时间记录一下上周遇到的一个问题,学习的脚步不能放慢,也不敢放慢。存在问题在线上环境进行服务压测,压测完成后,cpu使用率居高不下,很是费解,按理说已经没有压测请求了,这时消耗cpu资源的只有GC线程了,可以通过jstat命令查看一下JVM的GC情况,然后就碰到了诡异的GC问题。jstat命令jstat[…

    2022年6月19日
    24
  • opencv之打开摄像头、边缘检测

    按q退出if__name__==’__main__’:cap=cv2.VideoCapture(0)#设置摄像头0是默认的摄像头如果你有多个摄像头的话呢,可以设置1,2,3….whileTrue:#进入无限循环ret,frame=cap.read()#将摄像头拍到的图像作为frame值cv2….

    2022年4月7日
    43

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号