Java中通过反射执行一个方法的过程如下:获取一个方法对象,然后根据isAccessible 返回值确定是否能够执行,如果返回值为false则需要调用setAccessiblea(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.printIn("DoStuff").
}
}
定义一个public类的public方法,这是一个没有任何限制的方法,按照我们对Java语 言的理解,此时doStuff方法可以被任何一个类访问。我们编写一个客户端类来检査该方法是否可以反射执行:
public static void main(String[] args) throws Exception {
//反射获取方法
Method ml = Foo.class.getMethod("doStuff");
//打印出是否可访问
System.out.println("Accessible= "+ml.isAccessible());
//执行方法
ml.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 ReflectPermission{"suppresBAcceasChacks");
//是否重置了安全检查,默认是false
boolean override;
//默认构造函数
protected Accessible Object() {}
//是否可以快速获取,默认是不能
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.ensureMemberAcce88
}
//直接执行方法
return methodAccessor.invoke(obj,args);
}
看了这段代码,诸位就很清楚了: Accessible属性只是用来判断是否需要进行安全检査 的,如果不需要则直接执行,这就可以大幅度地提升系统性能(当然了,由于取消了安全检 査,也可以运行private方法、访问private私有属性了)。经过测试,在大量的反射情况下, 设置Accessible为true可以提升性能20倍以上。
AccessibleObject的其他两个子类 Field 和 Constructor 与 Method 的情形相似:Accessible 属性决定Field和Constructor是否受访问控制检査。我们在设置Field或执行Constructor时,务必要设置Accessible为true,这并不仅仅是因为操作习惯的问题,还是在为我们系统 的性能考虑。
注意对于我枘已经“习惯”了的代码,多思考一下为什么。