java反射的若幹高級應用 – JAVA編程語言程序開發技術文章

java的反射可以繞過訪問權限,訪問到類的非公有方法和成員。可能這點會引起安全性的討論。反射的使用幫助解決很多復雜的問題,其運行時的類型檢查,動態調用,代理的實現等,反射為我們寫程序帶來瞭很大的靈活性,很多功能都是基於反射。


利用反射還可以訪問內部類、匿名內部類的私有屬性。


 



下面提供一種利用安全管理器及反射訪問類非公有方法和成員的方法。


 


 


Java 運行時依靠一種安全性管理器來檢驗調用代碼對某一特定的訪問而言是否有足夠的權限。具體來說,安全性管理器是 java.lang.SecurityManager 類或擴展自該類的一個類,且它在運行時檢查某些應用程序操作的權限。換句話說,所有的對象訪問在執行自身邏輯之前都必須委派給安全管理器,當訪問受到安全性管理器的控制,應用程序就隻能執行那些由相關安全策略特別準許的操作。因此安全管理器一旦啟動可以為代碼提供足夠的保護。默認情況下,安全性管理器是沒有被設置的,除非代碼明確地安裝一個默認的或定制的安全管理器,否則運行時的訪問控制檢查並不起作用。我們可以通過這一點在運行時避開 Java 的訪問控制檢查,達到我們訪問非公有成員變量或方法的目的。為能訪問我們需要的非公有成員,我們還需要使用 Java 反射技術。Java 反射是一種強大的工具,它使我們可以在運行時裝配代碼,而無需在對象之間進行源代碼鏈接,從而使代碼更具靈活性。在編譯時,Java 編譯程序保證瞭私有成員的私有特性,從而一個類的私有方法和私有成員變量不能被其他類靜態引用。然而,通過 Java 反射機制使得我們可以在運行時查詢以及訪問變量和方法。由於反射是動態的,因此編譯時的檢查就不再起作用瞭。


下面的代碼演示瞭如何利用安全性管理器與反射機制訪問私有變量。


// 獲得指定變量的值
public static Object getValue(Object instance, String fieldName) throws IllegalAccessException, NoSuchFieldException {
Field field = getField(instance.getClass(), fieldName);
// 參數值為true,禁用訪問控制檢查
field.setAccessible(true);
return field.get(instance);
}
// 該方法實現根據變量名獲得該變量的值
public static Field getField(Class thisClass, String fieldName) throws NoSuchFieldException {
if (thisClass == null) {
   throw new NoSuchFieldException(“Error field !”);
}
}
 
 
 



其中 getField(instance.getClass(), fieldName) 通過反射機制獲得對象屬性,使用set方法可以重新設置變量的值,如field.set(instance, newValue); 。如果存在安全管理器,方法首先使用 this 和 Member.DECLARED 作為參數調用安全管理器的 checkMemberAccess 方法,這裡的 this 是 this 類或者成員被確定的父類。 如果該類在包中,那麼方法還使用包名作為參數調用安全管理器的 checkPackageAccess 方法。 每一次調用都可能導致 SecurityException。當訪問被拒絕時,這兩種調用方式都會產生 securityexception 異常 。


setAccessible(true) 方法通過指定參數值為 true 來禁用訪問控制檢查,從而使得該變量可以被其他類調用。我們可以在我們所寫的類中,擴展一個普通的基本類 java.lang.reflect.AccessibleObject 類。這個類定義瞭一種 setAccessible 方法,使我們能夠啟動或關閉對這些類中其中一個類的實例的接入檢測。這種方法的問題在於如果使用瞭安全性管理器,它將檢測正在關閉接入檢測的代碼是否允許這樣做。如果未經允許,安全性管理器拋出一個例外。


除訪問私有變量,我們也可以通過這個方法訪問私有方法。


public static Method getMethod(Object instance, String methodName, Class[] classTypes) throws NoSuchMethodException {
Method accessMethod = getMethod(instance.getClass(), methodName, classTypes);
// 參數值為true,禁用訪問控制檢查
accessMethod.setAccessible(true);
return accessMethod;
}
private static Method getMethod(Class thisClass, String methodName, Class[] classTypes) throws NoSuchMethodException {
if (thisClass == null) {
   throw new NoSuchMethodException(“Error method !”);
}
try {
   return thisClass.getDeclaredMethod(methodName, classTypes);
} catch (NoSuchMethodException e) {
   return getMethod(thisClass.getSuperclass(), methodName, classTypes);
}
}
 
 
 



獲得私有方法的原理與獲得私有變量的方法相同。當我們得到瞭函數後,需要對它進行調用,這時我們需要通過 invoke() 方法來執行對該函數的調用,代碼示例如下:


public static Object invokeMethod(Object instance, String methodName, Object arg) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
Object[] args = new Object[1];
args[0] = arg;
return invokeMethod(instance, methodName, args);
}
// 調用含多個參數的方法
public static Object invokeMethod(Object instance, String methodName, Object[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
Class[] classTypes = null;
if (args != null) {
   classTypes = new Class[args.length];
   for (int i = 0; i < args.length; i++) {
    if (args[i] != null) {
     classTypes[i] = args[i].getClass();
    }
   }
}
return getMethod(instance, methodName, classTypes).invoke(instance, args);
}
 
 
 



利用安全管理器及反射,可以在不修改源碼的基礎上訪問私有成員,為測試帶來瞭極大的方便。尤其是在編譯期間,該方法可以順利地通過編譯。但同時該方法也有一些缺點。第一個是性能問題,用於字段和方法接入時反射要遠慢於直接代碼。第二個是權限問題,有些涉及 Java 安全的程序代碼並沒有修改安全管理器的權限,此時本方法失效。


 


常見問題:


 


java反射調用靜態方法



Class c;
c = Class.forName(“class name”);
Method m = c.getMethod(“method name”, new Class[] { int.class, int.class, int.class, int.class });
m.invoke(c, new Object[] { 1, 2, 3, 4 });
 
 
 


 


利用反射取得泛型信息



一、傳統通過反射取得函數的參數和返回值


import java.lang.reflect.Method;
public class Foo {
public static void main(String[] args) throws Exception {
   Method[] methods = Foo.class.getDeclaredMethods();
   for (Method method : methods) {
    Class[] paramTypeList = method.getParameterTypes();


    Class returnType = method.getReturnType();
    System.out.println(returnType);
    for (Class clazz : paramTypeList) {
     System.out.println(clazz);
    }
    System.out.println();
   }
}


public static String test1(String str) {
   return null;
}


public static Integer test2(String str, Integer i) {
   return null;
}


}
 
 
 
 
二、在有泛型的時候,取得參數和返回值的集合類的泛型信息


import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.List;
public class Foo {
public static void main(String[] args) throws Exception {
   Method[] methods = Foo.class.getDeclaredMethods();
   for (Method method : methods) {
    System.out.println(” returnType: “);
    Type returnType = method.getGenericReturnType();
    if (returnType instanceof ParameterizedType) {
     Type[] types = ((ParameterizedType) returnType).getActualTypeArguments();
     for (Type type : types) {
      System.out.println(type);
     }
    }
    System.out.println(” paramTypeType: “);
    Type[] paramTypeList = method.getGenericParameterTypes();
    for (Type paramType : paramTypeList) {
     if (paramType instanceof ParameterizedType) {
      Type[] types = ((ParameterizedType) paramType).getActualTypeArguments();
      for (Type type : types) {
       System.out.println(type);
      }
     }
    }
 &nb

發佈留言