当前位置: 首页 > 编程笔记 >

InvocationHandler中invoke()方法的调用问题分析

松越
2023-03-14
本文向大家介绍InvocationHandler中invoke()方法的调用问题分析,包括了InvocationHandler中invoke()方法的调用问题分析的使用技巧和注意事项,需要的朋友参考一下

Java中动态代理的实现,关键就是这两个东西:Proxy、InvocationHandler,下面从InvocationHandler接口中的invoke方法入手,简单说明一下Java如何实现动态代理的。

首先,invoke方法的完整形式如下:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
  {
	method.invoke(obj, args);
	return null;
}

首先猜测一下,method是调用的方法,即需要执行的方法;args是方法的参数;proxy,这个参数是什么?以上invoke()方法的实现即是比较标准的形式,我们看到,这里并没有用到proxy参数。查看JDK文档中Proxy的说明,如下:

A method invocation on a proxy instance through one of its proxy interfaces will be dispatched to the invoke method of the instance's invocation handler, passing the proxy instance,a java.lang.reflect.Method object identifying the method that was invoked, and an array of type Object containing the arguments. 

由此可以知道以上的猜测是正确的,同时也知道,proxy参数传递的即是代理类的实例。

为了方便说明,这里写一个简单的例子来实现动态代理。

//抽象角色(动态代理只能代理接口) 
public interface Subject { 
   
  public void request(); 
} 
//真实角色:实现了Subject的request()方法 
public class RealSubject implements Subject{ 
   
  public void request(){ 
    System.out.println("From real subject."); 
  } 
} 
//实现了InvocationHandler 
public class DynamicSubject implements InvocationHandler 
{ 
  private Object obj;//这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象 
 
  public DynamicSubject() 
  { 
  } 
 
  public DynamicSubject(Object obj) 
  { 
    this.obj = obj; 
  } 
 
  //这个方法不是我们显示的去调用 
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
  { 
    System.out.println("before calling " + method); 
 
    method.invoke(obj, args); 
 
    System.out.println("after calling " + method); 
 
    return null; 
  } 
 
} 
//客户端:生成代理实例,并调用了request()方法 
public class Client { 
 
  public static void main(String[] args) throws Throwable{ 
    // TODO Auto-generated method stub 
 
    Subject rs=new RealSubject();//这里指定被代理类 
    InvocationHandler ds=new DynamicSubject(rs); 
    Class<?> cls=rs.getClass(); 
     
    //以下是一次性生成代理 
     
    Subject subject=(Subject) Proxy.newProxyInstance( 
        cls.getClassLoader(),cls.getInterfaces(), ds); 
     
    //这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口 
    System.out.println(subject instanceof Proxy); 
     
    //这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口 
    System.out.println("subject的Class类是:"+subject.getClass().toString()); 
     
    System.out.print("subject中的属性有:"); 
     
    Field[] field=subject.getClass().getDeclaredFields(); 
    for(Field f:field){ 
      System.out.print(f.getName()+", "); 
    } 
     
    System.out.print("\n"+"subject中的方法有:"); 
     
    Method[] method=subject.getClass().getDeclaredMethods(); 
     
    for(Method m:method){ 
      System.out.print(m.getName()+", "); 
    } 
     
    System.out.println("\n"+"subject的父类是:"+subject.getClass().getSuperclass()); 
     
    System.out.print("\n"+"subject实现的接口是:"); 
     
    Class<?>[] interfaces=subject.getClass().getInterfaces(); 
     
    for(Class<?> i:interfaces){ 
      System.out.print(i.getName()+", "); 
    } 
 
    System.out.println("\n\n"+"运行结果为:"); 
    subject.request(); 
  } 
} 

运行结果如下:此处省略了包名,***代替
true
subject的Class类是:class $Proxy0
subject中的属性有:m1, m3, m0, m2,
subject中的方法有:request, hashCode, equals, toString,
subject的父类是:class java.lang.reflect.Proxy
subject实现的接口是:cn.edu.ustc.dynamicproxy.Subject,

运行结果为:
before calling public abstract void ***.Subject.request()
From real subject.
after calling public abstract void ***.Subject.request()

PS:这个结果的信息非常重要,至少对我来说。因为我在动态代理犯晕的根源就在于将上面的subject.request()理解错了,至少是被表面所迷惑,没有发现这个subject和Proxy之间的联系,一度纠结于最后调用的这个request()是怎么和invoke()联系上的,而invoke又是怎么知道request存在的。其实上面的true和class$Proxy0就能解决很多的疑问,再加上下面将要说的$Proxy0的源码,完全可以解决动态代理的疑惑了。

从以上代码和结果可以看出,我们并没有显示的调用invoke()方法,但是这个方法确实执行了。下面就整个的过程进行分析一下:

从Client中的代码看,可以从newProxyInstance这个方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码:

public static Object newProxyInstance(ClassLoader loader, 
    Class<?>[] interfaces, 
    InvocationHandler h) 
throws IllegalArgumentException 
{ 
  if (h == null) { 
    throw new NullPointerException(); 
  } 
 
  /* 
   * Look up or generate the designated proxy class. 
   */ 
  Class cl = getProxyClass(loader, interfaces); 
 
  /* 
   * Invoke its constructor with the designated invocation handler. 
   */ 
  try { 
      /* 
      * Proxy源码开始有这样的定义: 
      * private final static Class[] constructorParams = { InvocationHandler.class }; 
      * cons即是形参为InvocationHandler类型的构造方法 
      */ 
    Constructor cons = cl.getConstructor(constructorParams); 
    return (Object) cons.newInstance(new Object[] { h }); 
  } catch (NoSuchMethodException e) { 
    throw new InternalError(e.toString()); 
  } catch (IllegalAccessException e) { 
    throw new InternalError(e.toString()); 
  } catch (InstantiationException e) { 
    throw new InternalError(e.toString()); 
  } catch (InvocationTargetException e) { 
    throw new InternalError(e.toString()); 
  } 
} 

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下几件事.
(1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0.$Proxy0类 实现了interfaces的接口,并继承了Proxy类.
(2)实例化$Proxy0并在构造方法中把DynamicSubject传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下:

class Proxy{ 
  InvocationHandler h=null; 
  protected Proxy(InvocationHandler h) { 
    this.h = h; 
  } 
  ... 
} 

来看一下这个继承了Proxy的$Proxy0的源代码:

public final class $Proxy0 extends Proxy implements Subject { 
  private static Method m1; 
  private static Method m0; 
  private static Method m3; 
  private static Method m2; 
 
  static { 
    try { 
      m1 = Class.forName("java.lang.Object").getMethod("equals", 
          new Class[] { Class.forName("java.lang.Object") }); 
 
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", 
          new Class[0]); 
 
      m3 = Class.forName("***.RealSubject").getMethod("request", 
          new Class[0]); 
 
      m2 = Class.forName("java.lang.Object").getMethod("toString", 
          new Class[0]); 
 
    } catch (NoSuchMethodException nosuchmethodexception) { 
      throw new NoSuchMethodError(nosuchmethodexception.getMessage()); 
    } catch (ClassNotFoundException classnotfoundexception) { 
      throw new NoClassDefFoundError(classnotfoundexception.getMessage()); 
    } 
  } //static 
 
  public $Proxy0(InvocationHandler invocationhandler) { 
    super(invocationhandler); 
  } 
 
  @Override 
  public final boolean equals(Object obj) { 
    try { 
      return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue(); 
    } catch (Throwable throwable) { 
      throw new UndeclaredThrowableException(throwable); 
    } 
  } 
 
  @Override 
  public final int hashCode() { 
    try { 
      return ((Integer) super.h.invoke(this, m0, null)).intValue(); 
    } catch (Throwable throwable) { 
      throw new UndeclaredThrowableException(throwable); 
    } 
  } 
 
  public final void request() { 
    try { 
      super.h.invoke(this, m3, null); 
      return; 
    } catch (Error e) { 
    } catch (Throwable throwable) { 
      throw new UndeclaredThrowableException(throwable); 
    } 
  } 
 
  @Override 
  public final String toString() { 
    try { 
      return (String) super.h.invoke(this, m2, null); 
    } catch (Throwable throwable) { 
      throw new UndeclaredThrowableException(throwable); 
    } 
  } 
} 

接着把得到的$Proxy0实例强制转换成Subject,并将引用赋给subject。当执行subject.request()方法时,就调用了$Proxy0类中的request()方法,进而调用父类Proxy中的h的invoke()方法.即InvocationHandler.invoke()。

PS:1、需要说明的一点是,Proxy类中getProxyClass方法返回的是Proxy的Class类。之所以说明,是因为我一开始犯了个低级错误,以为返回的是“被代理类的Class类”--!推荐看一下getProxyClass的源码,很长=。=

2、从$Proxy0的源码可以看出,动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法。

Q:到现在为止,还有一个疑问,invoke方法中的第一个参数是Proxy的实例(准确说,最终用到的是$Proxy0的实例),但是有什么用呢?或者说,程序内是怎样显示出作用的?

A:就本人目前的水平看来,这个proxy参数并没有什么作用,在整个动态代理机制中,并没有用到InvocationHandler中invoke方法的proxy参数。而传入的这个参数实际是代理类的一个实例。我想可能是为了让程序员在invoke方法中使用反射来获取关于代理类的一些信息吧。

总结

以上就是本文关于InvocationHandler中invoke()方法的调用问题分析的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:

Spring静态代理和动态代理代码详解

Spring框架依赖注入方法示例

Java编程实现springMVC简单登录实例

如有不足之处,欢迎留言指出。

 类似资料:
  • 我在我的程序中使用了实现继承,我的子类中的方法没有在主方法中被调用。它显示了错误“方法getArea()不在第二类型中定义”。getPerimeter()方法也有同样的问题。 我试过设置值和改变参数。 三角形。有关计算三角形面积的公式,请参见编程练习2.15。toString()方法的实现如下:return“Triangle:side1=“side1”side2=“side2”side3=“sid

  • 我创建了一个类,该类旨在获取2个分数,每个分数都有一个分子和分母,然后将它们相加,输出另一个分数。 当我编译程序时,我遇到了一个问题,涉及: 分数F3=新分数。添加(F1、F2);在主要方法中 错误:找不到类型分数$add 如果我将所有内容都设置为一个类,那么程序将运行,但我希望所有方法都严格位于Fraction类中,并在UseFraction类中调用Fraction。

  • 为什么下面的代码不能保证多个线程之间total_home数字的唯一性,即使逻辑处于同步块中。 } } } 这是一个程序示例。试着运行5-10次,你会发现total_home的值并不是每次都是唯一的。

  • 本文向大家介绍Android应用中clearFocus方法调用无效的问题解决,包括了Android应用中clearFocus方法调用无效的问题解决的使用技巧和注意事项,需要的朋友参考一下 clearFocus 无效? EditText在focus与非focus的时候,显示效果是不同的:focus的时候光标是闪的,而且我们通常也会给它设置selector,focus的时候给它加上边框之类的. 通常当

  • 我试图在我的中使用方法,我认为我有语法问题。到目前为止,这是我所拥有的: 我认为不能读取变量,这就是为什么它会给我一个错误。有什么建议吗?

  • 问题内容: Java 8的Java语言规范在“示例4.11-1。类型的用法”中提供了一个带有类型实参的方法调用示例: 在该示例中,提供的类型实参是有意义的,但显然用于方法调用的类型实参也可以是多余的,并且完全没有意义,并且甚至不需要涉及泛型。例如: 我有几个问题: 谁能提出Java允许这些冗余类型参数的正当理由?接受它们无害后,在我看来,编译器仍然可以并且应该抓住它们。 仅当带有类型实参的方法调用

  • 我正在学习kotlin和JavaFX。我想安排一个活动。 这段java代码可以工作: kontlin中的相同代码(不起作用): Kotlin:TableView.Edit(Pos.Row,Pos.tableColumn)方法tableColumn返回错误类型。 逐字:类型推断失败。预期的类型不匹配:推断的类型是TableColumn !但是TableColumn !,*>!是预期的 我做错了什么?

  • 问题内容: 我尝试在Android NDK上工作,我的第一个测试不是很确定,我需要帮助,因为我看不到错误在哪里。 以下代码可以毫无问题地进行编译,但是当在模拟器上运行时,该程序将返回SIGSEGV信号,并且在logcat中不会显式写入任何错误。但是,将显示一条警告,指出未找到Java类。经过一天的研究,这个问题看起来一切正确。 这是我的Java代码:JNITestActivity.java 这是我