当前位置: 首页 > 科技观察

避免使用ApacheBeanutils复制属性,为什么?一起来一探究竟

时间:2023-03-22 01:57:38 科技观察

大家好,我是哪吒。今天通过四大工具类的代码示例、源码解读、横向对比,跟大家聊一聊对象赋值的问题。在实际项目开发中,对象之间的赋值是很常见的。随着双十一、秒杀等电商流程越来越复杂,数据量也越来越大,效率问题也随之浮出水面。Q:如果让你来写对象间赋值的代码,你会怎么做?答:想也不想,直接上代码,get和set就可以了。问:喜欢下图吗?A:是啊,你怎么能把我的代码放到网上呢?问:不,我只是举个例子。A:这涉及商业秘密,这是一个非常严重的问题。问:我发现你可以胡说八道。你能直接回答问题吗?答:行行行,我也觉得这样写很low。上次这样写完,差点被太多东西撞到,ctrlc+strlv,键盘差点没坏;而且很容易出错,一不留神,上面的属性不对应,赋值错误;代码看起来很蠢,一个类有几千行,全部复制get和set,甚至命名为transfer,我觉得很优雅;需要添加每个属性的含义注释(基本上就是要添加这种赋值操作,注释很重要,注释很重要,注释很重要);代码维护起来很麻烦;对象过多会出现类爆问题,属性过多会严重违反阿里巴巴代码协议(一个方法的实际代码最多20行);问:好吧,好吧,告诉我,如何解决。答:很简单,直接通过工具类Beanutils赋值就可以Q:听说最近工具类很复杂,你用的是哪一个?答:Apache自带的很简单。为了您的欣赏,我手写了一篇。问题:你的代码报错,避免使用ApacheBeanutils复制属性。答:不报错,只是严重警告,只要代码能运行,有问题再优化。问:你的态度是什么?人事抽签的人为什么会有严重警告?答:拿多少钱,干多少活,我不是XXX,应该是业绩问题问:具体原因是什么?答:3000元,你非要撕掉apachecopyProperties的源码?通过单例模式调用copyProperties,但是每个方法对应一个BeanUtilsBean.getInstance()实例,每个类实例对应一个实例,不是真正的单例模式。publicstaticvoidcopyProperties(Objectdest,Objectorig)throwsIllegalAccessException,InvocationTargetException{BeanUtilsBean.getInstance().copyProperties(dest,orig);}性能瓶颈-->日志太多也是一种病你可以通过源码看到,每个copyProperties都要做多次类型检查,还要打印日志。publicvoidcopyProperties(Objectdest,Objectorig)throwsIllegalAccessException,InvocationTargetException{//类型检查if(dest==null){thrownewIllegalArgumentException("Nodestinationbeanspecified");}elseif(orig==null){thrownewIllegalArgumentException("Nooriginbeanspecified");}else{//打印日志if(this.log.isDebugEnabled()){this.log.debug("BeanUtils.copyProperties("+dest+","+orig+")");}intvar5;变量6;字符串名称;对象值;//类型检查//DanyBean提供动态修改实现类的属性名、属性值、属性类型的功能if(originstanceofDynaBean){//获取源对象的所有属性DynaProperty[]origDescriptors=((DynaBean)orig).getDynaClass().getDynaProperties();DynaProperty[]var4=origDescriptors;var5=origDescriptors.length;for(var6=0;var6springcglibBeanCopier优于springcopyProperties优于apachePropertyUtils优于apacheBeanUtils避免使用ApacheBeanutils复制属性的问题。ApachePropertyUtils源码分析从源码可以清楚的看出,类型检查变成了非空检查,并且去掉了每次拷贝的日志记录,性能肯定更好。类型检查变成了非空检查,每个副本的日志记录都被去掉了。实际赋值由copyProperty改为DanyBean+setSimpleProperty;DanyBean提供了一种动态修改实现它的类的属性名、属性值和属性类型的方法。功能。publicvoidcopyProperties(Objectdest,Objectorig){//判断数据源和目标对象是否不为nullif(dest==null){thrownewIllegalArgumentException("Nodestinationbeanspecified");}elseif(orig==null){thrownewIllegalArgumentException("Nooriginbeanspecified");}else{//删除org.apache.commons.beanutils.BeanUtils.copyProperties中最耗时的日志记录intvar5;变量6;字符串名称;对象值;//类型检查if(originstanceofDynaBean){//获取源对象的所有属性DynaProperty[]origDescriptors=((DynaBean)orig).getDynaClass().getDynaProperties();DynaProperty[]var4=origDescriptors;var5=原始描述符。长度;for(var6=0;var6editable,@NullableString...ignoreProperties){//判断数据源和目标对象是否不为nullAssert.notNull(source,"Sourcemust不为空");Assert.notNull(target,"目标不能为空");/***如果target设置了泛型,默认使用泛型*如果editable为null,这里忽略*一般editable默认为null*/ClassactualEditable=target.getClass();if(editable!=null){if(!editable.isInstance(target)){thrownewIllegalArgumentException("目标类["+target.getClass().getName()+"]不可分配给可编辑类["+editable.getName()+"]");}actualEditable=editable;}//获取目标中的所有属性描述PropertyDescriptor[]targetPds=getPropertyDescriptors(actualEditable);//要忽略的List属性ignoreList=(ignoreProperties!=null?Arrays.asList(ignoreProper领带):空);for(PropertyDescriptortargetPd:targetPds){方法writeMethod=targetPd.getWriteMethod();//目标对象有write方法,属性不被忽略.getClass(),targetPd.getName());if(sourcePd!=null){方法readMethod=sourcePd.getReadMethod();/***源对象有read方法,可以复制数据*writeMethod.getParameterTypes()[0]:获取writeMethod的第一个入参类型*readMethod.getReturnType():获取readMethod的返回值类型*判断返回值类型与入参类型是否存在继承关系,只有存在继承关系或相等时才会进行注入*/if(readMethod!=null&&ClassUtils.isAssignable(writeMethod.getParameterTypes()[0],readMethod.getReturnType())){//放开读取方法的权限if(!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())){readMethod.setAccessible(true);}//通过反射获取值Objectvalue=readMethod.invoke(source);//释放写方法的权限if(!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())){writeMethod.setAccessible(true);}//通过反射写入值writeMethod.invoke(target,value);}}}}}总结阿里友情提醒,避免用ApacheBeanutils复制对象,还是很有道理的。ApacheBeanutils的性能问题出现在每个副本的类型验证和日志记录中。ApachePropertyUtils做了如下优化:类型检查变成了非空检查。删除每个副本的日志记录。实际赋值由copyProperty改为DanyBean+setSimpleProperty。SpringcopyProperties做了如下优化:将判断数据源和目标对象的非空判断改为断言。每个副本都没有记录。没有if(originstanceofDynaBean){这个类型检查。添加了释放权限的步骤。本文转载自微信公众号《哪吒编程》,可通过以下二维码关注。转载本文请联系哪吒编程公众号。