首页 » 编写高质量代码:改善Java程序的151个建议 » 编写高质量代码:改善Java程序的151个建议全文在线阅读

《编写高质量代码:改善Java程序的151个建议》建议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,这并不仅仅是因为操作习惯的问题,还是在为我们系统的性能考虑。

注意 对于我们已经“习惯”了的代码,多思考一下为什么。