javascript
java reflectionutils_Spring中的各种Utils(五):ReflectionUtils详解(转载)
原文出處:https://blog.csdn.net/wolfcode_cn/article/details/80660515
原創文章,轉載請注明出處。
本節中,我們來看看Spring針對反射提供的工具類:ReflectionUtils。反射在容器中使用是非常頻繁的了,ReflectionUtils中也提供了想當多的有用的方法,一起來看看。
在ReflectionUtils中提供了一些專門用于處理在反射中異常相關的方法,這些方法一般在Spring框架內部使用,當然,出于規范考慮,我們在開發中涉及到反射的異常,也可以使用這些方法。我們按照這些方法的調用鏈來看代碼:
void handleReflectionException(Exception ex)
處理反射中的異常,我們直接看代碼:
public static void handleReflectionException(Exception ex) {
if (ex instanceof NoSuchMethodException) {
throw new IllegalStateException("Method not found: " + ex.getMessage());
}
if (ex instanceof IllegalAccessException) {
throw new IllegalStateException("Could not access method: " + ex.getMessage());
}
if (ex instanceof InvocationTargetException) {
handleInvocationTargetException((InvocationTargetException) ex);
}
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
throw new UndeclaredThrowableException(ex);
}
可以看到,代碼把NoSuchMethodException,IllegalAccessException,InvocationTargetException等反射中的檢查異常(Exception)直接包裝為對應的運行時異常(RuntimeException);這里我們可以先看一下反射中的異常:
可以看到,反射中的異常,都繼承了ReflectiveOperationException(反射操作異常),而該異常繼承了Exception;旗下再是ClassNotFoundException等具體的反射操作異常;
在方法中,如果遇到的是InvocationTargetException異常,交給handleInvocationTargetException方法處理:
public static void rethrowRuntimeException(Throwable ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
if (ex instanceof Error) {
throw (Error) ex;
}
throw new UndeclaredThrowableException(ex);
}
可以看到,該方法只是判斷了運行時異常和Error;并原樣拋出;怎么理解這個方法的調用?原因很簡單,InvocationTargetException是在method.invoke的時候拋出的,方法在執行的過程中,方法本身的執行可能拋出RuntimeException或者Error,其余方法本身拋出的Exception異常直接包裝為UndeclaredThrowableException(RuntimeException)處理;
統一下來,可以這樣理解,除了在反射執行過程中遇到的Error,其余所有的Exception,都被統一轉成了RuntimeException;
接下來進入正常方法
Field findField(Class> clazz, String name, Class> type)
根據類型,字段名稱和字段類型查詢一個字段;該方法會遍歷的向父類查詢字段,查詢到的是所有字段;我們可以簡單看一下實現:
public static Field findField(Class> clazz, String name, Class> type) {
Class> searchType = clazz;
while (Object.class != searchType && searchType != null) {
Field[] fields = getDeclaredFields(searchType);
for (Field field : fields) {
if ((name == null || name.equals(field.getName())) &&
(type == null || type.equals(field.getType()))) {
return field;
}
}
searchType = searchType.getSuperclass();
}
return null;
}
代碼實現比較簡單,向上查詢字段,直到Object類型;注意,其中調用了一句代碼:
Field[] fields = getDeclaredFields(searchType);
我們來看看該代碼實現:
private static Field[] getDeclaredFields(Class> clazz) {
Field[] result = declaredFieldsCache.get(clazz);
if (result == null) {
result = clazz.getDeclaredFields();
declaredFieldsCache.put(clazz, (result.length == 0 ? NO_FIELDS : result));
}
return result;
}
可以看到,實際上,在該工具類中,對類型和字段做了緩存,保存到了declaredFieldsCache中,來看看這個cache的聲明:
private static final Map, Field[]> declaredFieldsCache =
new ConcurrentReferenceHashMap, Field[]>(256);
因為是工具類,被聲明為ConcurrentReferenceHashMap也是能夠理解;
這種緩存機制也存在于方法的查詢;
該方法還有一個簡單的版本:
Field findField(Class> clazz, String name)
void setField(Field field, Object target, Object value)
在指定對象(target)中給指定字段(field)設置指定值(value);
Object getField(Field field, Object target)
在指定對象(target)上得到指定字段(field)的值;
以上兩個方法的實現都非常簡單,分別調用了field.set和field.get方法;并處理了對應的異常;選一個實現看看:
public static void setField(Field field, Object target, Object value) {
try {
field.set(target, value);
}
catch (IllegalAccessException ex) {
handleReflectionException(ex);
throw new IllegalStateException(
"Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage());
}
}
使用了handleReflectionException方法來統一處理異常;
Method findMethod(Class> clazz, String name, Class>… paramTypes)
在類型clazz上,查詢name方法,參數類型列表為paramTypes;
public static Method findMethod(Class> clazz, String name, Class>... paramTypes) {
Class> searchType = clazz;
while (searchType != null) {
Method[] methods = (searchType.isInterface() ? searchType.getMethods() : getDeclaredMethods(searchType));
for (Method method : methods) {
if (name.equals(method.getName()) &&
(paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
return method;
}
}
searchType = searchType.getSuperclass();
}
return null;
}
可以看到,該仍然可以向上遞歸查詢方法,并且查詢到的是所有方法。
該方法也有一個簡單的版本:
Method findMethod(Class> clazz, String name)
Object invokeMethod(Method method, Object target, Object… args)
在指定對象(target)上,使用指定參數(args),執行方法(method);
該方法的實現也非常簡單:
public static Object invokeMethod(Method method, Object target, Object... args) {
try {
return method.invoke(target, args);
}
catch (Exception ex) {
handleReflectionException(ex);
}
throw new IllegalStateException("Should never get here");
}
可以看到,就只是調用了method.invoke方法,并統一處理了調用異常;
該方法也有一個簡單版本:
Object invokeMethod(Method method, Object target)
boolean declaresException(Method method, Class> exceptionType)
判斷一個方法上是否聲明了指定類型的異常;
boolean isPublicStaticFinal(Field field)
判斷字段是否是public static final的;
boolean isEqualsMethod(Method method)
判斷方法是否是equals方法;
boolean isHashCodeMethod(Method method)
判斷方法是否是hashcode方法;
boolean isToStringMethod(Method method)
判斷方法是否是toString方法;
可能有童鞋記得,在AopUtils中也有這幾個isXXX方法,是的,其實AopUtils中的isXXX方法就是調用的ReflectionUtils的這幾個方法的;
boolean isObjectMethod(Method method)
判斷方法是否是Object類上的方法;
void makeAccessible(Field field)
將一個字段設置為可讀寫,主要針對private字段;
void makeAccessible(Method method)
將一個方法設置為可調用,主要針對private方法;
void makeAccessible(Constructor> ctor)
將一個構造器設置為可調用,主要針對private構造器;
void doWithLocalMethods(Class> clazz, MethodCallback mc)
針對指定類型上的所有方法,依次調用MethodCallback回調;
首先來看看MethodCallback接口聲明:
public interface MethodCallback {
/**
* 使用指定方法完成一些操作.
*/
void doWith(Method method) throws IllegalArgumentException, IllegalAccessException;
}
其實就是一個正常的回調接口;來看看doWithLocalMethods實現:
public static void doWithLocalMethods(Class> clazz, MethodCallback mc) {
Method[] methods = getDeclaredMethods(clazz);
for (Method method : methods) {
try {
mc.doWith(method);
}catch (IllegalAccessException ex) {
throw new IllegalStateException("...");
}
}
}
其實實現很簡單,就是得到類上的所有方法,然后執行回調接口;這個方法在Spring針對bean的方法上的標簽處理時大量使用,比如@Init,@Resource,@Autowire等標簽的預處理;
該方法有一個增強版:
void doWithMethods(Class> clazz, MethodCallback mc, MethodFilter mf)
該版本提供了一個方法匹配(過濾器)MethodFilter;
我們來看看MethodFilter的接口聲明:
public interface MethodFilter {
/**
* 檢查一個指定的方法是否匹配規則
*/
boolean matches(Method method);
}
該接口就聲明了一個匹配方法,用于匹配規則;
再返回來看看doWithMethods方法的實現:
public static void doWithMethods(Class> clazz, MethodCallback mc, MethodFilter mf) {
// Keep backing up the inheritance hierarchy.
Method[] methods = getDeclaredMethods(clazz);
for (Method method : methods) {
if (mf != null && !mf.matches(method)) {
continue;
}
try {
mc.doWith(method);
}catch (IllegalAccessException ex) {
throw new IllegalStateException("...");
}
}
if (clazz.getSuperclass() != null) {
doWithMethods(clazz.getSuperclass(), mc, mf);
}else if (clazz.isInterface()) {
for (Class> superIfc : clazz.getInterfaces()) {
doWithMethods(superIfc, mc, mf);
}
}
}
該方法實現就很明確了,首先得到類上所有方法,針對每一個方法,調用MethodFilter實現匹配檢查,如果匹配上,調用MethodCallback回調方法。該方法會遞歸向上查詢所有父類和實現的接口上的所有方法并處理;
void doWithLocalFields(Class> clazz, FieldCallback fc)
那很明顯,該方法就是針對所有的字段,執行的對應的回調了,這里的FieldCallback就類似于前面的MethodCallback:
public interface FieldCallback {
/**
* 給指定的字段執行操作;
*/
void doWith(Field field) throws IllegalArgumentException, IllegalAccessException;
}
該方法的實現就類似于doWithLocalMethods的實現了:
public static void doWithLocalFields(Class> clazz, FieldCallback fc) {
for (Field field : getDeclaredFields(clazz)) {
try {
fc.doWith(field);
}catch (IllegalAccessException ex) {
throw new IllegalStateException("...");
}
}
}
得到類上所有的字段,并執行回調;同理,該方法在Spring中主要用于預處理字段上的@Autowire或者@Resource標簽;
void doWithFields(Class> clazz, FieldCallback fc, FieldFilter ff)
和doWithMethods的加強版相同,針對字段,也提供了一個擁有字段匹配(過濾)的功能方法;我們就只簡單看下FieldFilter實現即可:
public interface FieldFilter {
/**
* 檢查給定字段是否匹配;
*/
boolean matches(Field field);
}
小結
總的來說,ReflectionUtils提供的功能還算完整,其實想要實現這樣的一個工具類,也不是什么難事,更多的建議大家多看看Spring的實現,還是有不少收獲。當然,這里我們看的是Spring4.X的代碼,相信在Spring5中完全使用Java8的代碼,會更優雅。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的java reflectionutils_Spring中的各种Utils(五):ReflectionUtils详解(转载)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么要学习Java EE?需要掌握哪些
- 下一篇: springmvc跳转html_Spri