讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議103:反射訪問屬性或方法時將Accessible設置為true >

建議103:反射訪問屬性或方法時將Accessible設置為true

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,這並不僅僅是因為操作習慣的問題,還是在為我們系統的性能考慮。

注意 對於我們已經「習慣」了的代碼,多思考一下為什麼。