java反射調(diào)用get/set方法實(shí)現(xiàn)
1 前言
最新工作中,遇到了通過(guò)反射調(diào)用get/set方法的地方,雖然反射的性能不是很好,但是相比較于硬編碼的不易擴(kuò)展,getDeclareFields可以拿到所有的成員變量,后續(xù)添加或刪除成員變量時(shí),不用修改代碼,且應(yīng)用次數(shù)只在修改數(shù)據(jù)時(shí)使用,故犧牲一些性能提高擴(kuò)展性
2 傳統(tǒng)的方式
見過(guò)很多人通過(guò)反射調(diào)用get/set方法都是通過(guò)獲取屬性的name,然后通過(guò)字符串截取將首字母大寫,再拼上get/set來(lái)做
String fieldName = field.getName(); String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
也可以通過(guò)fieldName轉(zhuǎn)成字符數(shù)組,首個(gè)字符-32來(lái)避免字符串截取的
String fieldName = field.getName(); char[] chars = fieldName.toCharArray(); chars[0] = (char)(chars[0] - 32); String getMethodName = "get" + new String(chars);
我覺得兩種方式都可以,但是不知道有沒有遇到過(guò),生成的get/set方法并不是已get/set開頭的,而是以is開頭的,比如boolean類型的成員變量。這個(gè)時(shí)候我們就需要去判斷屬性的類型,然后用不同的前綴來(lái)拼接get/set方法名。其實(shí),在jdk中已經(jīng)包含了這樣的工具類
3 Introspector和PropertyDescriptor
關(guān)于這兩個(gè)類的詳細(xì)介紹,我這里就不說(shuō)了,簡(jiǎn)單的理解就是對(duì)象信息的描述,里面提供了一些API方便我們拿到對(duì)象的信息
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();
//過(guò)濾掉不需要修改的屬性
return !"class".equals(name) && !"id".equals(name);
}).collect(Collectors.toList());
for (PropertyDescriptor descriptor : descriptors) {
//descriptor.getWriteMethod()方法對(duì)應(yīng)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,其實(shí)就是對(duì)于get/set方法,至于方法名稱不需要我們來(lái)關(guān)于,這樣就可以避免方法名拼錯(cuò)的情況了。 另外PropertyDescriptor除了可以通過(guò)Introspector獲取,也可以自己new來(lái)創(chuàng)建,其構(gòu)造方法還是比較全的

通常傳遞一個(gè)屬性的名稱和類對(duì)象class就可以了
List<Field> fields = Arrays.stream(template.getClass().getDeclaredFields()).filter(f -> {
String name = f.getName();
//過(guò)濾掉不需要修改的屬性
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();
}
}
通過(guò)上面兩種不同的實(shí)現(xiàn)方式可以看到,Introspector會(huì)額外有一個(gè)class屬性,但是類似serialVersionUID不會(huì)算在內(nèi);而自定義PropertyDescriptor需要通過(guò)反射拿到所有的屬性,雖然不會(huì)有class屬性,但是serialVersionUID會(huì)算在內(nèi),使用的時(shí)候需要注意一下。 如果你以為這就是Introspector的全部功能,那就大錯(cuò)特錯(cuò)了。Introspector不同于普通的反射,反射一次,一段時(shí)間內(nèi)可重復(fù)使用,為什么不是永久呢,看下源碼
/**
* 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;
}
注意中間加粗標(biāo)紅的代碼,這里除了同步之外,還做了一個(gè)本地的緩存
BeanInfo getBeanInfo(Class<?> type) {
return (this.beanInfoCache != null)
? this.beanInfoCache.get(type)
: null;
}
這個(gè)beanInfoCache 其實(shí)是一個(gè)WeakHashMap,每次gc被回收,所以上面說(shuō)一段時(shí)間內(nèi)可以重復(fù)使用而不是永久,也是為了避免OOM吧
到此這篇關(guān)于java反射調(diào)用get/set方法實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)java反射調(diào)用get/set內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java通過(guò)PropertyDescriptor反射調(diào)用set和get方法
- Java如何加載外部Jar的類并通過(guò)反射調(diào)用類的方法
- java反射調(diào)用方法NoSuchMethodException的解決方案
- java 反射調(diào)用Service導(dǎo)致Spring注入Dao失效的解決方案
- Java 使用反射調(diào)用jar包中的類方式
- Java使用反射調(diào)用方法示例
- java中利用反射調(diào)用另一類的private方法的簡(jiǎn)單實(shí)例
- Java 反射調(diào)用靜態(tài)方法的簡(jiǎn)單實(shí)例
- 反射調(diào)用private方法實(shí)踐(php、java)
相關(guān)文章
如何利用SpringAOP的返回通知處理數(shù)據(jù)加密返回
這篇文章主要介紹了如何利用SpringAOP的返回通知處理數(shù)據(jù)加密返回,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2024-12-12
jtds1.1連接sqlserver2000測(cè)試示例
這篇文章主要介紹了jtds1.1連接sqlserver2000測(cè)試示例,需要的朋友可以參考下2014-02-02
Java后端Spring?Boot全局異常處理最佳實(shí)踐記錄
Spring Boot通過(guò)和提供強(qiáng)大的異常處理機(jī)制,支持統(tǒng)一REST和頁(yè)面錯(cuò)誤響應(yīng),這篇文章主要介紹了Java后端Spring?Boot全局異常處理的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-08-08
基于Springboot使用logback的注意事項(xiàng)
這篇文章主要介紹了Springboot使用logback的注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
使用SpringBoot+OkHttp+fastjson實(shí)現(xiàn)Github的OAuth第三方登錄
這篇文章主要介紹了使用SpringBoot+OkHttp+fastjson實(shí)現(xiàn)Github的OAuth第三方登錄,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
SpringBoot應(yīng)用啟動(dòng)流程源碼解析
這篇文章主要介紹了SpringBoot應(yīng)用啟動(dòng)流程源碼解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04

