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,这并不仅仅是因为操作习惯的问题,还是在为我们系统的性能考虑。
注意 对于我们已经“习惯”了的代码,多思考一下为什么。