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简单登录实例

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

您可能感兴趣的文章:

  • 详解Java中Method的Invoke方法
  • Java反射机制及Method.invoke详解
(0)

相关推荐

  • Java反射机制及Method.invoke详解

    JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类:在运行时构造任意一个类的对象:在运行时判断任意一个类所具有的成员变量和方法:在运行时调用任意一个对象的方法:生成动态代理. 1. 得到某个对象的属性 复制代码 代码如下: public Object get

  • 详解Java中Method的Invoke方法

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

  • 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; } 首先

  • C#中分部方法和分部类分析

    本文实例分析了C#中分部方法和分部类.分享给大家供大家参考. 具体代码如下: 复制代码 代码如下: using System; namespace Partial {     class Program     {         static void Main(string[] args)         {             A a = new A();         }     } partial class A     {         public A()        

  • Java集合中contains方法的效率对比分析

    最近让部门技术大佬帮忙代码review的时候,他给我指出了一个小的技术细节,就是对于集合的contains方法尽量选用Set而不是List,平时没怎么注意,仔细看了下源码,大佬就是大佬,技术细节也把握的死死的. Java集合List.Set中均有对集合中元素是否存在的判断方法contains(Object o):Map中有对key及value是否存在的判断方法containsKey(Object key)和containsValue(Object value). 1.ArrayList 在Arr

  • 深入解析Java中反射中的invoke()方法

    先讲一下java中的反射: 反射就是将类别的各个组成部分进行剖析,可以得到每个组成部分,就可以对每一部分进行操作 反射机制应用场景:逆向代码.动态生成类框架等,使用反射机制能够大大的增强程序的扩展性. 反射的基本步骤:首先获得Class对象,然后实例化对象,获得类的属性.方法或者构造函数,最后访问属性.调用方法.调用构造函数创建对象.而invoke()方法就是用来执行指定对象的方法. 在比较复杂的程序或框架中来使用反射技术,可以简化代码提高程序的复用性. 讲的是Method类的invoke()方

  • Java的invoke方法的具体使用

    如果读一些Java或者相关框架的源码,实际上一定会经常出现invoke方法的调用,在自己或者团队封装框架时,如果有时候弄得不好经常也会报invoke相关的错. invoke方法是干什么的?有什么具体用途? 首先要了解invoke方法干什么的以及具体用途,实际你要搞清他在源码那个class文件上,他在那个包里,追根溯源. invoke方法来自Method类,可能不会像我们经常用到的基础类型包装类,以及集合类还有他们的扩展和工具类使用的那么多. 但是Method类所在的包可是大名鼎鼎的反射Refla

  • php中的动态调用实例分析

    本文实例讲述了php中的动态调用具体用法.分享给大家供大家参考.具体分析如下: 在程序中如果加一大堆判断的确是一个很大的麻烦,例如这样: 复制代码 代码如下: if($fun='a'){echo "哎呀!";} elesif(){} -- else{echo "嗯!";} 真的很麻烦并且造成程序后期阅读和修改时候的巨大麻烦,这时候我们可以把每一个要执行的代码段,用函数来实现,然后可以用一个更加NB的方法来实现这些功能,并且因为每一个函数实现一个功能,我们维护起来就简

  • javascript中函数作为参数调用的方法

    本文实例讲述了javascript中函数作为参数调用的方法.分享给大家供大家参考.具体分析如下: 先来看示例: function Map(){ var obj = {}; this.put = function(key, value){ obj[key] = value; } this.eachMap = function(fn){ for(var attr in obj){ fn(attr, obj[attr]); } } } var m = new Map(); m.put('01', 'a

  • jquery中filter方法用法实例分析

    本文实例讲述了jquery中filter方法用法.分享给大家供大家参考.具体分析如下: filter()方法将匹配元素集合缩减为匹配指定选择器的元素. filter方法中的参数可以为字符串值,包含供匹配当前元素集合的选择器表达式.   一.filter的参数类型可分为两种   1.传递选择器 $('a').filter('.external')   2.传递过滤函数 复制代码 代码如下: $('a').filter(function(index) {         return $(this)

  • C#中WPF使用多线程调用窗体组件的方法

    本文实例讲述了C#中WPF使用多线程调用窗体组件的方法.分享给大家供大家参考.具体如下: Thread thread=new Thread(new ThreadStart(TestThread)); thread.Start(); private void TestThread() { for (int i = 0; i < 11;i++ ) { Thread.Sleep(2000); this.listBox1.Dispatcher.Invoke(new Action(() => { thi

随机推荐