在这里可以通过创造被动收入颠覆八小时工作制,最终实现财务自由。

解决BeanUtils和PropertyUtils的From对象里的null值会覆盖To对象里非空值的问题,提高复制效率和性能 如何高效率实现一个复制对象属性方法工具?

薪薪向荣 JackLeon 3周前 (02-27) 77次浏览 0个评论 扫描二维码

痛点描述:我们用BeanUtil或PropertyUtils里的copyProperties()的方法或者我们自己用ReflectASM的高性能的反射编写自己的copyProperties()的方法,我发现把具有相同属性名的B对象复制到A对象,假如A对象的name属性有值,而B对象里的属性name为null,则B会把A里的name值覆盖掉,使A的name为null,这个不是我们所期望的,我们希望当A里的属性不为空时不被B覆盖,于是就有了下面的解决方案:

解决BeanUtils和PropertyUtils的From对象里的null值会覆盖To对象里非空值的问题,提高复制效率和性能 如何高效率实现一个复制对象属性方法工具?

解决BeanUtils和PropertyUtils的From对象里的null值会覆盖To对象里非空值的问题,提高复制效率和性能 如何高效率实现一个复制对象属性方法工具?

背景:

在商业项目中所需要的业务非常多,所以我们的业务数据也会有很多种,这个时候就会有什么VO,DTO,PO等等这些,把业务和我们的基础数据进行分离转换。但是一直都没有什么好一点的转换类。后来用了一下BeanUtils.copyPropertie,和PropertyUtils.copyProperties()的方法,发现其效率非常低。这里也简单总结了一下他们的用法及原理以及自己实现的转换类;用法:BeanUtils.copyProperties("转换后的类", "要转换的类");PropertyUtils.copyProperties("转换后的类", "要转换的类");用法其实很简单,第一个参数是转换后的类,第二个参数是待转换的类;我们可以理解成为后转前;原理:其原理是通过JDK自带的反射机制动态的去get,set,从而去转换我们的类。但是要注意一点他们所支持的数据类型,还有一个就是假如一个类里面又写了一个类,例如这种:public class Name{}class Name1{}一般叫做内部类,像这种类进行转换的时候,是不会成功的。因为在里面进行读写校验的时候不会通过;
PropertyDescriptor[] origDescriptors = getPropertyDescriptors(orig); for (int i = 0; i < origDescriptors.length; i++) { String name = origDescriptors[i].getName(); if (isReadable(orig, name) && isWriteable(dest, name)) { try { Object value = getSimpleProperty(orig, name); if (dest instanceof DynaBean) { ((DynaBean) dest).set(name, value); } else { setSimpleProperty(dest, name, value); } } catch (NoSuchMethodException e) { if (log.isDebugEnabled()) { log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e); } } } }

上面是JDK的源代码,我们执行isRead和isWrite的时候并不会通过;注意差异,主要的区别在于BeanUtils提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,而前者不支持这个功能,但是速度会更快一些。BeanUtils支持的转换类型如下:* java.lang.BigDecimal* java.lang.BigInteger* boolean and java.lang.Boolean* byte and java.lang.Byte* char and java.lang.Character* java.lang.Class* double and java.lang.Double* float and java.lang.Float* int and java.lang.Integer* long and java.lang.Long* short and java.lang.Short* java.lang.String* java.sql.Date* java.sql.Time* java.sql.Timestamp阅读其源码,发现其内部是使用了装饰者模式,我发现Java得工具类很喜欢使用这种模式,而且也十分好用;他们都使用到了BeanutilsBean和PropertyUtilsBean只不过BeanUtils多了一个转换的功能而已,但是性能上要比PropertyUtils慢一些,其实两个都很慢,最好不要使用;

重写反射转换:


/** * @param obj 转换的对象值 * @param clz 类对象 * @return 转换后的对象 */public static<T> T transferObject(Object obj,Class clz){ T result = null; if(obj!=null&&!obj.equals("")){ Method[] methods = obj.getClass().getMethods(); try { result = (T)clz.newInstance(); } catch (Exception e1) { return null; } Method m; for(int i=0;i<methods.length;i++){ m = methods[i]; try { if(m.getName().startsWith("set")){ String fieldName = m.getName().replaceFirst("set", ""); Method method = result.getClass().getMethod(m.getName(), m.getParameterTypes()); Method getMethod = obj.getClass().getMethod("get"+fieldName, new Class[]{}); method.invoke(result, getMethod.invoke(obj, new Object[]{})); } } catch (Exception e) { continue; } } } return result;}

上面这个方法也是用了Java反射去写的,但是少了很多校验以及转换。所以在100万条数据的时候,效率是3739毫秒,而使用BeanUtils是5000毫秒左右。两个效率都不高;

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类。 ASM反射转换:
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几毫秒,效率已经没问题了;---------------------  通过以上分析总结,假如我们用BeanUtil或PropertyUtils里的copyProperties()的方法或者我们自己用ReflectASM的高性能的反射编写自己的copyProperties()的方法我发现把具有相同属性名的B对象复制到A对象,假如A对象的name属性有值,而B对象里的属性name为null,则B会把A里的name值覆盖掉,使A的name为null,这个不是我们所期望的,于是就有了下面的解决方案:

解决BeanUtils和PropertyUtils的From对象里的null值会覆盖To对象里非空值的问题,提高复制效率和性能

¥1.88查看全文 购买VIP查看全站
喜欢 (0)
[1186664388@qq.com]
分享 (0)
关于作者:
创享视界(creativeview.cn)是一个带动全民颠覆八小时工作制,通过投稿把自己的创意智慧变现的方式创造被动收入,从而实现财务自由的平台。我们相信,创新思维不仅有助于打造更出色的产品,还可以让世界变得更美好,让人人受益。
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
%d 博主赞过: