1 前言
最新工作中,遇到了通过反射调用get/set方法的地方,虽然反射的性能不是很好,但是相比较于硬编码的不易扩展,getdeclarefields可以拿到所有的成员变量,后续添加或删除成员变量时,不用修改代码,且应用次数只在修改数据时使用,故牺牲一些性能提高扩展性
2 传统的方式
见过很多人通过反射调用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中已经包含了这样的工具类
3 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就可以了
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吧
到此这篇关于java反射调用get/set方法实现的文章就介绍到这了,更多相关java反射调用get/set内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论