加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

详解Java中Method的Invoke方法

发布时间:2020-05-25 06:55:32 所属栏目:Java 来源:互联网
导读:在写代码的时候,发现从父类class通过getDeclaredMethod获取的Method可以调用子类的对象,而子类改写了这个方法,从子类class通过getDeclaredMethod也能获取到Method,这时去调用父类的对象也会报错。虽然这是很符合

在写代码的时候,发现从父类class通过getDeclaredMethod获取的Method可以调用子类的对象,而子类改写了这个方法,从子类class通过getDeclaredMethod也能获取到Method,这时去调用父类的对象也会报错。虽然这是很符合多态的现象,也符合java的动态绑定规范,但还是想弄懂java是如何实现的,就学习了下Method的源代码。 
Method的invoke方法 

1.先检查 AccessibleObject的override属性是否为true。

AccessibleObject是Method,Field,Constructor的父类,override属性默认为false,可调用setAccessible方法改变,如果设置为true,则表示可以忽略访问权限的限制,直接调用。

2.如果不是ture,则要进行访问权限检测。用Reflection的quickCheckMemberAccess方法先检查是不是public的,如果不是再用Reflection.getCallerClass(1)方法获

得到调用这个方法的Class,然后做是否有权限访问的校验,校验之后缓存一次,以便下次如果还是这个类来调用就不用去做校验了,直接用上次的结果,(很奇怪用这种方式缓存,因为这种方式如果下次换个类来调用的话,就不用会缓存了,而再验证一遍,把这次的结果做为缓存,但上一次的缓存结果就被冲掉了。这是一个很简单的缓冲机制,只适用于一个类的重复调用)。 

3.调用MethodAccessor的invoke方法。每个Method对象包含一个root对象,root对象里持有一个MethodAccessor对象。我们获得的Method独享相当于一个root对象的镜像,所有这类Method共享root里的MethodAccessor对象,(这个对象由ReflectionFactory方法生成,ReflectionFactory对象在Method类中是static final的由native方法实例化)。

ReflectionFactory生成MethodAccessor:如果noInflation的属性为true则直接返回MethodAccessorGenerator创建的一个MethodAccessor。否则返回DelegatingMethodAccessorImpl,并将他与一个NativeMethodAccessorImpl互相引用。但DelegatingMethodAccessorImpl执行invoke方法的时候又委托给NativeMethodAccessorImpl了。

再一步深入

4.NativeMethodAccessorImpl的invkoe方法:

调用natiave方法invoke0执行方法调用.

注意这里有一个计数器numInvocations,每调用一次方法+1,当比 ReflectionFactory.inflationThreshold(15)大的时候,用MethodAccessorGenerator创建一个MethodAccessor,并把之前的DelegatingMethodAccessorImpl引用替换为现在新创建的。下一次DelegatingMethodAccessorImpl就不会再交给NativeMethodAccessorImpl执行了,而是交给新生成的java字节码的MethodAccessor。

MethodAccessorGenerator使用了asm字节码动态加载技术,暂不深入研究。 

总结 一个方法可以生成多个Method对象,但只有一个root对象,主要用于持有一个MethodAccessor对象,这个对象也可以认为一个方法只有一个,相当于是static的。因为Method的invoke是交给MethodAccessor执行的,所以我所想要知道的答案在MethodAccessor的invoke中,深入MethodAccessor: 

------------------------------------------MethodAccessor---------------------------------
假如有这么一个类A: 

public class A {
  public void foo(String name) {
    System.out.println("Hello," + name);
  }
}

可以编写另外一个类来反射调用A上的方法: 

import java.lang.reflect.Method;
public class TestClassLoad {
  public static void main(String[] args) throws Exception {
    Class<?> clz = Class.forName("A");
    Object o = clz.newInstance();
    Method m = clz.getMethod("foo",String.class);
    for (int i = 0; i < 16; i++) {
      m.invoke(o,Integer.toString(i));
    }
  }
}

注意到TestClassLoad类上不会有对类A的符号依赖――也就是说在加载并初始化TestClassLoad类时不需要关心类A的存在与否,而是等到main()方法执行到调用Class.forName()时才试图对类A做动态加载;这里用的是一个参数版的forName(),也就是使用当前方法所在类的ClassLoader来加载,并且初始化新加载的类。……好吧这个细节跟主题没啥关系。 

回到主题。这次我的测试环境是Sun的JDK 1.6.0 update 13 build 03。编译上述代码,并在执行TestClassLoad时加入-XX:+TraceClassLoading参数(或者-verbose:class或者直接-verbose都行),如下:

控制台命令

java -XX:+TraTestClassLoad  ceClassLoading 

可以看到输出了一大堆log,把其中相关的部分截取出来如下:

[Loaded TestClassLoad from file:/D:/temp_code/test_java_classload/]
[Loaded A from file:/D:/temp_code/test_java_classload/]
[Loaded sun.reflect.NativeMethodAccessorImpl from shared objects file]
[Loaded sun.reflect.DelegatingMethodAccessorImpl from shared objects file]
Hello,0
Hello,1
Hello,2
Hello,3
Hello,4
Hello,5
Hello,6
Hello,7
Hello,8
Hello,9
Hello,10
Hello,11
Hello,12
Hello,13
Hello,14
[Loaded sun.reflect.ClassFileConstants from shared objects file]
[Loaded sun.reflect.AccessorGenerator from shared objects file]
[Loaded sun.reflect.MethodAccessorGenerator from shared objects file]
[Loaded sun.reflect.ByteVectorFactory from shared objects file]
[Loaded sun.reflect.ByteVector from shared objects file]
[Loaded sun.reflect.ByteVectorImpl from shared objects file]
[Loaded sun.reflect.ClassFileAssembler from shared objects file]
[Loaded sun.reflect.UTF8 from shared objects file]
[Loaded java.lang.Void from shared objects file]
[Loaded sun.reflect.Label from shared objects file]
[Loaded sun.reflect.Label$PatchInfo from shared objects file]
[Loaded java.util.AbstractList$Itr from shared objects file]
[Loaded sun.reflect.MethodAccessorGenerator$1 from shared objects file]
[Loaded sun.reflect.ClassDefiner from shared objects file]
[Loaded sun.reflect.ClassDefiner$1 from shared objects file]
[Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__]
Hello,15

可以看到前15次反射调用A.foo()方法并没有什么稀奇的地方,但在第16次反射调用时似乎有什么东西被触发了,导致JVM新加载了一堆类,其中就包括[Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__]这么一行。这是哪里来的呢? 

先来看看JDK里Method.invoke()是怎么实现的。 

java.lang.reflect.Method: 
public final
  class Method extends AccessibleObject implements GenericDeclaration,Member {
  // ...
  private volatile MethodAccessor methodAccessor;
  // For sharing of MethodAccessors. This branching structure is
  // currently only two levels deep (i.e.,one root Method and
  // potentially many Method objects pointing to it.)
  private Method       root;
  // ...
  public Object invoke(Object obj,Object... args)
      throws IllegalAccessException,IllegalArgumentException,InvocationTargetException
  {
    if (!override) {
      if (!Reflection.quickCheckMemberAccess(clazz,modifiers)) {
        Class caller = Reflection.getCallerClass(1);
        Class targetClass = ((obj == null || !Modifier.isProtected(modifiers))
                   ? clazz
                   : obj.getClass());
        boolean cached;
        synchronized (this) {
          cached = (securityCheckCache == caller)
            && (securityCheckTargetClassCache == targetClass);
        }
        if (!cached) {
          Reflection.ensureMemberAccess(caller,clazz,obj,modifiers);
          synchronized (this) {
          securityCheckCache = caller;
          securityCheckTargetClassCache = targetClass;
          }
        }
      }
    }
    if (methodAccessor == null) acquireMethodAccessor();
    return methodAccessor.invoke(obj,args);
  }
  // NOTE that there is no synchronization used here. It is correct
  // (though not efficient) to generate more than one MethodAccessor
  // for a given Method. However,avoiding synchronization will
  // probably make the implementation more scalable.
  private void acquireMethodAccessor() {
    // First check to see if one has been created yet,and take it
    // if so
    MethodAccessor tmp = null;
    if (root != null) tmp = root.getMethodAccessor();
    if (tmp != null) {
      methodAccessor = tmp;
      return;
    }
    // Otherwise fabricate one and propagate it up to the root
    tmp = reflectionFactory.newMethodAccessor(this);
    setMethodAccessor(tmp);
  }
  // ...
}

  可以看到Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理。 

每个实际的Java方法只有一个对应的Method对象作为root,。这个root是不会暴露给用户的,而是每次在通过反射获取Method对象时新创建Method对象把root包装起来再给用户。在第一次调用一个实际Java方法对应得Method对象的invoke()方法之前,实现调用逻辑的MethodAccessor对象还没创建;等第一次调用时才新创建MethodAccessor并更新给root,然后调用MethodAccessor.invoke()真正完成反射调用。 

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读