java反射调用get/set方法,你还在拼接方法名吗?
前言
最新工作中,遇到了通过反射调用get/set方法的地方,虽然反射的性能不是很好,但是相比较于硬编码的不易扩展,getDeclareFields可以拿到所有的成员变量,后续添加或删除成员变量时,不用修改代码,且应用次数只在修改数据时使用,故牺牲一些性能提高扩展性
传统的方式
见过很多人通过反射调用get/set方法都是通过获取属性的name,然后通过字符串截取将首字母大写,再拼上get/set来做
String fieldName = field.getName();
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
还有稍微好一点的同学,通过fieldName转成字符数组,首个字符-32来避免字符串截取的
String fieldName = field.getName(); char[] chars = fieldName.toCharArray(); chars[0] = (char)(chars[0] - 32); String getMethodName = "get" + new String(chars);
诚然,我觉得两种方式都可以,但是不知道有没有遇到过,生成的get/set方法并不是已get/set开头的,而是以is开头的,比如boolean类型的成员变量。这个时候我们就需要去判断属性的类型,然后用不同的前缀来拼接get/set方法名。其实,在jdk中已经包含了这样的工具类
Introspector和PropertyDescriptor
关于这两个类的详细介绍,我这里就不说了,简单的理解就是对象信息的描述,里面提供了一些API方便我们拿到对象的信息
BeanInfo beanInfo; try { beanInfo = Introspector.getBeanInfo(template.getClass()); } catch (IntrospectionException e) { log.info("xxxxxxxxxxxxxxxx", e); return null; } List<PropertyDescriptor> descriptors = Arrays.stream(beanInfo.getPropertyDescriptors()).filter(p -> { String name = p.getName(); //过滤掉不需要修改的属性 return !"class".equals(name) && !"id".equals(name); }).collect(Collectors.toList()); for (PropertyDescriptor descriptor : descriptors) { //descriptor.getWriteMethod()方法对应set方法 Method readMethod = descriptor.getReadMethod(); System.out.println(descriptor.getName()); try { Object o = readMethod.invoke(template); System.out.println(o); } catch (IllegalAccessException | InvocationTargetException e) { log.info("xxxxxxxxxxxxxxxx", e); return null; } }
PropertyDescriptor类提供了getReadMethod和getWriteMethod,其实就是对于get/set方法,至于方法名称不需要我们来关于,这样就可以避免方法名拼错的情况了。
另外PropertyDescriptor除了可以通过Introspector获取,也可以自己new来创建,其构造方法还是比较全的
通常传递一个属性的名称和类对象class就可以了
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
List<Field> fields = Arrays.stream(template.getClass().getDeclaredFields()).filter(f -> { String name = f.getName(); //过滤掉不需要修改的属性 return !"id".equals(name) && !"serialVersionUID".equals(name); }).collect(Collectors.toList()); for (Field field : fields) { try { PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), template.getClass()); Method readMethod = descriptor.getReadMethod(); Object o = readMethod.invoke(template); System.out.println(o); } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } }
通过上面两种不同的实现方式可以看到,Introspector会额外有一个class属性,但是类似serialVersionUID不会算在内;而自定义PropertyDescriptor需要通过反射拿到所有的属性,虽然不会有class属性,但是serialVersionUID会算在内,使用的时候需要注意一下。
如果你以为这就是Introspector的全部功能,那就大错特错了。Introspector不同于普通的反射,反射一次,一段时间内可重复使用,为什么不是永久呢,看下源码
/** * Introspect on a Java Bean and learn about all its properties, exposed * methods, and events. * <p> * If the BeanInfo class for a Java Bean has been previously Introspected * then the BeanInfo class is retrieved from the BeanInfo cache. * * @param beanClass The bean class to be analyzed. * @return A BeanInfo object describing the target bean. * @exception IntrospectionException if an exception occurs during * introspection. * @see #flushCaches * @see #flushFromCaches */ public static BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException { if (!ReflectUtil.isPackageAccessible(beanClass)) { return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo(); } ThreadGroupContext context = ThreadGroupContext.getContext(); BeanInfo beanInfo; synchronized (declaredMethodCache) { beanInfo = context.getBeanInfo(beanClass); } if (beanInfo == null) { beanInfo = new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo(); synchronized (declaredMethodCache) { context.putBeanInfo(beanClass, beanInfo); } } return beanInfo; }
注意中间加粗标红的代码,这里除了同步之外,还做了一个本地的缓存
BeanInfo getBeanInfo(Class<?> type) { return (this.beanInfoCache != null) ? this.beanInfoCache.get(type) : null; }
这个beanInfoCache 其实是一个WeakHashMap,每次gc被回收,所以上面说一段时间内可以重复使用而不是永久,也是为了避免OOM吧
总结
大概先说这么多吧,虽然算不上什么高级技术,但是能将工作中遇到的小问题解决也是成长啊!
学问:纸上得来终觉浅,绝知此事要躬行
为事:工欲善其事,必先利其器。
态度:道阻且长,行则将至;行而不辍,未来可期
.....................................................................
------- 桃之夭夭,灼灼其华。之子于归,宜其室家。 ---------------
------- 桃之夭夭,有蕡其实。之子于归,宜其家室。 ---------------
------- 桃之夭夭,其叶蓁蓁。之子于归,宜其家人。 ---------------
=====================================================================
* 博客文章部分截图及内容来自于学习的书本及相应培训课程以及网络其他博客,仅做学习讨论之用,不做商业用途。
* 如有侵权,马上联系我,我立马删除对应链接。 * @author Alan -liu * @Email no008@foxmail.com
转载请标注出处! ✧*꧁一品堂.技术学习笔记꧂*✧. ---> https://www.cnblogs.com/ios9/