Java中通過反射執行一個方法的過程如下:獲取一個方法對象,然後根據isAccessible返回值確定是否能夠執行,如果返回值為false則需要調用setAccessible(true),最後再調用invoke執行方法,具體如下:
Method method=……;
//檢查是否可以訪問
if(!method.isAccessible()){
method.setAccessible(true);
}
//執行方法
method.invoke(obj, args);
此段代碼已經成為了習慣用法:通過反射方式執行方法時,必須在invoke之前檢查Accessible屬性。這是一個好習慣,也確實應該如此,但方法對象的Accessible屬性並不是用來決定是否可訪問的,看如下代碼:
public class Foo{
public final void doStuff(){
System.out.println("Do Stuff……");
}
}
定義一個public類的public方法,這是一個沒有任何限制的方法,按照我們對Java語言的理解,此時doStuff方法可以被任何一個類訪問。我們編寫一個客戶端類來檢查該方法是否可以反射執行:
public static void main(Stringargs)throws Exception{
//反射獲取方法
Method m1=Foo.class.getMethod("doStuff");
//打印出是否可訪問
System.out.println("Accessible="+m1.isAccessible());
//執行方法
m1.invoke(new Foo());
}
很簡單的反射操作,獲得一個方法,然後檢查是否可以訪問,最後執行方法輸出。讓我們來猜想一下結果:因為Foo類是public的,方法也是public,全部都是最開放的訪問權限,那麼Accessible也應該等於true。但是運行結果卻是:
Accessible=false
Do Stuff……
為什麼Accessible屬性會等於false?而且等於false了還能執行?這是因為Accessible的屬性並不是我們語法層級理解的訪問權限,而是指是否更容易獲得,是否進行安全檢查。
我們知道,動態修改一個類或方法或執行方法時都會受Java安全體系的制約,而安全的處理是非常消耗資源的(性能非常低),因此對於運行期要執行的方法或要修改的屬性就提供了Accessible可選項:由開發者決定是否要逃避安全體系的檢查。
閱讀源代碼是理解的最好方式,我們來看AccessibleObject類的源代碼,它提供了取消默認訪問控制檢查的功能。首先查看isAccessible方法,代碼如下:
public class AccessibleObject implements AnnotatedElement{
//定義反射的默認操作權限:suppressAccessChecks
static final private java.security.Permission ACCESS_PERMISSION=new Reflect-
Permission("suppressAccessChecks");
//是否重置了安全檢查,默認是false
boolean override;
//默認構造函數
protected AccessibleObject(){}
//是否可以快速獲取,默認是不能
public boolean isAccessible(){
return override;
}
}
AccessibleObject是Field、Method、Constructor的父類,決定其是否可以快速訪問而不進行訪問控制檢查,在AccessibleObject類中是以override變量保存該值的,但是具體是否快速執行是在Method類的invoke方法中決定的,代碼如下:
public Object invoke(Object obj, Object……args)throws……{
//檢查是否可以快速獲取,其值是父類AccessibleObject的override變量
if(!override){
//不能快速獲取,要進行安全檢查
if(!Reflection.quickCheckMemberAccess(……){
……
Reflection.ensureMemberAccess(……);
……
}
}
//直接執行方法
return methodAccessor.invoke(obj, args);
}
看了這段代碼,諸位就很清楚了:Accessible屬性只是用來判斷是否需要進行安全檢查的,如果不需要則直接執行,這就可以大幅度地提升系統性能(當然了,由於取消了安全檢查,也可以運行private方法、訪問private私有屬性了)。經過測試,在大量的反射情況下,設置Accessible為true可以提升性能20倍以上。
AccessibleObject的其他兩個子類Field和Constructor與Method的情形相似:Accessible屬性決定Field和Constructor是否受訪問控制檢查。我們在設置Field或執行Constructor時,務必要設置Accessible為true,這並不僅僅是因為操作習慣的問題,還是在為我們系統的性能考慮。
注意 對於我們已經「習慣」了的代碼,多思考一下為什麼。