Java get set 同步_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方便我们拿到对象的信息

48304ba5e6f9fe08f3fa1abda7d326ab.png

BeanInfo beanInfo;try{

beanInfo=Introspector.getBeanInfo(template.getClass());

}catch(IntrospectionException e) {

log.info("xxxxxxxxxxxxxxxx", e);return null;

}

List 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;

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

PropertyDescriptor类提供了getReadMethod和getWriteMethod,其实就是对于get/set方法,至于方法名称不需要我们来关于,这样就可以避免方法名拼错的情况了。

另外PropertyDescriptor除了可以通过Introspector获取,也可以自己new来创建,其构造方法还是比较全的

d9373ffc982aff59f5beb2e81baa88ea.png

通常传递一个属性的名称和类对象class就可以了

48304ba5e6f9fe08f3fa1abda7d326ab.png

import java.beans.PropertyDescriptor;import java.lang.reflect.Method;List 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= newPropertyDescriptor(field.getName(), template.getClass());

Method readMethod=descriptor.getReadMethod();

Object o=readMethod.invoke(template);

System.out.println(o);

}catch (IntrospectionException | IllegalAccessException |InvocationTargetException e) {

e.printStackTrace();

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

通过上面两种不同的实现方式可以看到,Introspector会额外有一个class属性,但是类似serialVersionUID不会算在内;而自定义PropertyDescriptor需要通过反射拿到所有的属性,虽然不会有class属性,但是serialVersionUID会算在内,使用的时候需要注意一下。

如果你以为这就是Introspector的全部功能,那就大错特错了。Introspector不同于普通的反射,反射一次,一段时间内可重复使用,为什么不是永久呢,看下源码

48304ba5e6f9fe08f3fa1abda7d326ab.png

/*** Introspect on a Java Bean and learn about all its properties, exposed

* methods, and events.

*

* If the BeanInfo class for a Java Bean has been previously Introspected

* then the BeanInfo class is retrieved from the BeanInfo cache.

*

*@parambeanClass The bean class to be analyzed.

*@returnA BeanInfo object describing the target bean.

*@exceptionIntrospectionException if an exception occurs during

* introspection.

*@see#flushCaches

*@see#flushFromCaches*/

public static BeanInfo getBeanInfo(Class>beanClass)throwsIntrospectionException

{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);

}

}returnbeanInfo;

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

注意中间加粗标红的代码,这里除了同步之外,还做了一个本地的缓存

BeanInfo getBeanInfo(Class>type) {return (this.beanInfoCache != null)? this.beanInfoCache.get(type)

:null;

}

这个beanInfoCache

其实是一个WeakHashMap,每次gc被回收,所以上面说一段时间内可以重复使用而不是永久,也是为了避免OOM吧

总结

大概先说这么多吧,虽然算不上什么高级技术,但是能将工作中遇到的小问题解决也是成长啊!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值