反射访问属性或方法时将Accessible设置为true

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,这并不仅仅是因为操作习惯的问题,还是在为我们系统 的性能考虑。

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

你可能感兴趣的:(java程序的151个建议)