java反射調(diào)用get/set方法實(shí)現(xiàn)
1 前言
最新工作中,遇到了通過反射調(diào)用get/set方法的地方,雖然反射的性能不是很好,但是相比較于硬編碼的不易擴(kuò)展,getDeclareFields可以拿到所有的成員變量,后續(xù)添加或刪除成員變量時(shí),不用修改代碼,且應(yīng)用次數(shù)只在修改數(shù)據(jù)時(shí)使用,故犧牲一些性能提高擴(kuò)展性
2 傳統(tǒng)的方式
見過很多人通過反射調(diào)用get/set方法都是通過獲取屬性的name,然后通過字符串截取將首字母大寫,再拼上get/set來做
String fieldName = field.getName(); String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
也可以通過fieldName轉(zhuǎn)成字符數(shù)組,首個(gè)字符-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類型的成員變量。這個(gè)時(shí)候我們就需要去判斷屬性的類型,然后用不同的前綴來拼接get/set方法名。其實(shí),在jdk中已經(jīng)包含了這樣的工具類
3 Introspector和PropertyDescriptor
關(guān)于這兩個(gè)類的詳細(xì)介紹,我這里就不說了,簡(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();
//過濾掉不需要修改的屬性
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方法,至于方法名稱不需要我們來關(guān)于,這樣就可以避免方法名拼錯(cuò)的情況了。 另外PropertyDescriptor除了可以通過Introspector獲取,也可以自己new來創(chuàng)建,其構(gòu)造方法還是比較全的

通常傳遞一個(gè)屬性的名稱和類對(duì)象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();
}
}
通過上面兩種不同的實(shí)現(xiàn)方式可以看到,Introspector會(huì)額外有一個(gè)class屬性,但是類似serialVersionUID不會(huì)算在內(nèi);而自定義PropertyDescriptor需要通過反射拿到所有的屬性,雖然不會(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被回收,所以上面說一段時(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通過PropertyDescriptor反射調(diào)用set和get方法
- Java如何加載外部Jar的類并通過反射調(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)文章
Spring?使用注解存儲(chǔ)和讀取?Bean對(duì)象操作方法
在?Spring?中,要想更加簡(jiǎn)單的實(shí)現(xiàn)對(duì)?Bean?對(duì)象的儲(chǔ)存和使用,其核心就是使用?注解?,本文主要就是演示如何使用注解實(shí)現(xiàn)對(duì)?Bean?對(duì)象的存取操作,感興趣的朋友跟隨小編一起看看吧2023-08-08
詳解Spring緩存注解@Cacheable,@CachePut , @CacheEvict使用
這篇文章主要介紹了詳解Spring緩存注解@Cacheable,@CachePut , @CacheEvict使用,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05
Java實(shí)現(xiàn)讀取文件夾下(包括子目錄)所有文件的文件名
這篇文章主要介紹了Java實(shí)現(xiàn)讀取文件夾下(包括子目錄)所有文件的文件名,本文把代碼組織成了一個(gè)模塊,可以很方便的使用,需要的朋友可以參考下2015-06-06
springboot-mongodb的多數(shù)據(jù)源配置的方法步驟
這篇文章主要介紹了springboot-mongodb的多數(shù)據(jù)源配置的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
淺談SpringCloud feign的http請(qǐng)求組件優(yōu)化方案
這篇文章主要介紹了淺談SpringCloud feign的http請(qǐng)求組件優(yōu)化方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-02-02
從零開始學(xué)SpringBoot如何開始使用圖文詳解
這篇文章主要介紹了從零開始學(xué)SpringBoot如何開始使用,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
JUC并發(fā)編程LinkedBlockingQueue隊(duì)列深入分析源碼
LinkedBlockingQueue 是一個(gè)可選有界阻塞隊(duì)列,這篇文章主要為大家詳細(xì)介紹了Java中LinkedBlockingQueue的實(shí)現(xiàn)原理與適用場(chǎng)景,感興趣的可以了解一下2023-04-04
springmvc+kindeditor文件上傳實(shí)例詳解
這篇文章主要為大家詳細(xì)介紹了springmvc+kindeditor文件上傳實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08

