- 浏览: 506099 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
gaoke:
"我觉得这是java动态生成代码的方式得到的,因为使 ...
InvocationHandler中invoke()方法的调用问题 -
lyandyhk:
可以,反正对于我这个水平来说刚刚好,正好全部看懂,满分
InvocationHandler中invoke()方法的调用问题 -
593844923:
Subject subject=(Subject) Proxy ...
InvocationHandler中invoke()方法的调用问题 -
hl174:
写的不错 源码确实有点长 第一次大致看还有些没看怎么明白
InvocationHandler中invoke()方法的调用问题 -
draem0507:
129应该表示为00000000 10000001,转成byt ...
Java的补码表示
以下的内容部分参考了网络上的内容,在此对原作者表示感谢!
Java中动态代理的实现,关键就是这两个东西:Proxy、InvocationHandler,下面从InvocationHandler接口中的invoke方法入手,简单说明一下Java如何实现动态代理的。
首先,invoke方法的完整形式如下:
首先猜测一下,method是调用的方法,即需要执行的方法;args是方法的参数;proxy,这个参数是什么?以上invoke()方法的实现即是比较标准的形式,我们看到,这里并没有用到proxy参数。查看JDK文档中Proxy的说明,如下:
由此可以知道以上的猜测是正确的,同时也知道,proxy参数传递的即是代理类的实例。
为了方便说明,这里写一个简单的例子来实现动态代理。
PS:这个结果的信息非常重要,至少对我来说。因为我在动态代理犯晕的根源就在于将上面的subject.request()理解错了,至少是被表面所迷惑,没有发现这个subject和Proxy之间的联系,一度纠结于最后调用的这个request()是怎么和invoke()联系上的,而invoke又是怎么知道request存在的。其实上面的true和class $Proxy0就能解决很多的疑问,再加上下面将要说的$Proxy0的源码,完全可以解决动态代理的疑惑了。
从以上代码和结果可以看出,我们并没有显示的调用invoke()方法,但是这个方法确实执行了。下面就整个的过程进行分析一下:
从Client中的代码看,可以从newProxyInstance这个方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码:
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下几件事.
(1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0.$Proxy0类 实现了interfaces的接口,并继承了Proxy类.
(2)实例化$Proxy0并在构造方法中把DynamicSubject传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下:
来看一下这个继承了Proxy的$Proxy0的源代码:
接着把得到的$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方法中使用反射来获取关于代理类的一些信息吧。
你好!这里是否可以理解为java根据你提供给他的cls的接口文件自己造出来一个实现这个接口的类,然后这个类由于实现Subject接口可作为Subject接收,然后后面的调用便是实现这个类的方法呢?
同求,请问$Proxy0的源码是如何还原出来的???
我觉得这是java动态生成代码的方式得到的,因为使用了反射,这样会自动生成代码和编译,生成的Proxy0应该在工程文件夹里面找,应该有
这是字节码生成技术自动上生成的,可以看java虚拟机类的加载那个章节有介绍
同求,请问$Proxy0的源码是如何还原出来的???
我觉得这是java动态生成代码的方式得到的,因为使用了反射,这样会自动生成代码和编译,生成的Proxy0应该在工程文件夹里面找,应该有
同求,请问$Proxy0的源码是如何还原出来的???
额,我写的东西感觉很少能帮到别人……受宠若惊
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方法中使用反射来获取关于代理类的一些信息吧。
评论
12 楼
gaoke
2016-11-17
"我觉得这是java动态生成代码的方式得到的,因为使用了反射,这样会自动生成代码和编译,生成的Proxy0应该在工程文件夹里面找,应该有",这是错误的,
仔细看一下代理类的参数,Subject subject=(Subject) Proxy.newProxyInstance(
cls.getClassLoader(),cls.getInterfaces(), ds);
代理的是接口,而没有任何实现类,所以不可能从代码层面“变出”方法内执行逻辑,而是只生成一个方法名称,而为什么调用代理类的方法也能执行方法逻辑呢? 使用代理类的时候你也写出来了,注意这行InvocationHandler ds=new DynamicSubject(rs); 你传入了一个实现类实例,而在调用代理类的方法时,实际上是回调传参通过反射调用这个实例的方法逻辑执行的,所以说代理类之所以称为代理,并没有从实质上“copy”一个实例,而是对真正的实例对象进行了一层传递“调用”。
仔细看一下代理类的参数,Subject subject=(Subject) Proxy.newProxyInstance(
cls.getClassLoader(),cls.getInterfaces(), ds);
代理的是接口,而没有任何实现类,所以不可能从代码层面“变出”方法内执行逻辑,而是只生成一个方法名称,而为什么调用代理类的方法也能执行方法逻辑呢? 使用代理类的时候你也写出来了,注意这行InvocationHandler ds=new DynamicSubject(rs); 你传入了一个实现类实例,而在调用代理类的方法时,实际上是回调传参通过反射调用这个实例的方法逻辑执行的,所以说代理类之所以称为代理,并没有从实质上“copy”一个实例,而是对真正的实例对象进行了一层传递“调用”。
11 楼
lyandyhk
2016-04-09
可以,反正对于我这个水平来说刚刚好,正好全部看懂,满分
10 楼
593844923
2016-03-07
Subject subject=(Subject) Proxy.newProxyInstance( cls.getClassLoader(),cls.getInterfaces(), ds);
你好!这里是否可以理解为java根据你提供给他的cls的接口文件自己造出来一个实现这个接口的类,然后这个类由于实现Subject接口可作为Subject接收,然后后面的调用便是实现这个类的方法呢?
9 楼
hl174
2015-12-01
写的不错 源码确实有点长 第一次大致看还有些没看怎么明白
8 楼
fanfan159357
2014-08-04
tbdp 写道
ly_ltw 写道
suoos 写道
我想请问一个我迷惑的问题:继承了Proxy的$Proxy0的源代码是如何获得的??
同求,请问$Proxy0的源码是如何还原出来的???
我觉得这是java动态生成代码的方式得到的,因为使用了反射,这样会自动生成代码和编译,生成的Proxy0应该在工程文件夹里面找,应该有
这是字节码生成技术自动上生成的,可以看java虚拟机类的加载那个章节有介绍
7 楼
tbdp
2014-07-03
ly_ltw 写道
suoos 写道
我想请问一个我迷惑的问题:继承了Proxy的$Proxy0的源代码是如何获得的??
同求,请问$Proxy0的源码是如何还原出来的???
我觉得这是java动态生成代码的方式得到的,因为使用了反射,这样会自动生成代码和编译,生成的Proxy0应该在工程文件夹里面找,应该有
6 楼
ly_ltw
2014-02-21
suoos 写道
我想请问一个我迷惑的问题:继承了Proxy的$Proxy0的源代码是如何获得的??
同求,请问$Proxy0的源码是如何还原出来的???
5 楼
suoos
2013-10-13
我想请问一个我迷惑的问题:继承了Proxy的$Proxy0的源代码是如何获得的??
4 楼
firefaith
2013-10-11
我想知道怎么method中的代码是怎么获得的?
3 楼
NO.6
2013-08-09
很细化,看了之后对动态代理理解加深了
2 楼
paddy.w
2012-09-16
lxl631 写道
最近正在看代理,楼主的文章写的太好了,把我的疑惑都解决了
额,我写的东西感觉很少能帮到别人……受宠若惊
1 楼
lxl631
2012-09-03
最近正在看代理,楼主的文章写的太好了,把我的疑惑都解决了
发表评论
-
Hive自定义UDAF详解
2014-07-25 14:14 14716遇到一个Hive ... -
编译libhdfs
2014-02-25 15:32 3782Mysql Applier是Mysql向hdf ... -
Java序列化机制对同一对象的处理
2012-04-20 18:47 0以下例子参考http://developer. ... -
Java的补码表示
2012-04-20 17:19 3989最近发现一个比较低级的问题,就是java中的数 ... -
同步锁的失败可能
2012-04-20 10:29 1100以下例子参考http://developer.51cto.co ... -
SpringExt 扩展原理
2011-07-25 11:27 9048这篇文章是基于webx框架官方文档整理的。具体 ... -
优化多线程执行效率
2011-05-22 21:38 1714摘抄自http://zhmocean.itey ... -
ExecutorService与Executors例子的简单剖析
2011-05-11 00:13 6599对于多线程有了一点了解之后,那么来看看java ... -
java深度历险
2011-04-17 20:57 1316InfoQ上面有几篇文章不错,适合已经对jav ... -
Java引用类型
2011-04-05 19:00 5484Java有四种引 ... -
double类型运算精度丢失
2011-04-04 15:45 5625前段时间看了一点python入门,写了几个运算 ... -
Java变量初始化
2011-03-29 15:01 1483先看一个例子: public class Te ... -
Java实现循环移位
2011-03-25 20:56 5955做MD5算法时遇到了循环移位,在网上找了写资料 ... -
Java简记
2011-03-18 20:17 1814这里随手做一些记录,以便有空时整理。 ... -
Java的栈和堆
2011-03-12 21:18 1381学的越多, ... -
String的一点东西
2011-03-12 20:24 1037关于String类的一点东西,算是对容易疏忽的 ... -
Html转换为PDF
2011-03-11 13:37 27001、html文件转为xhtml文件 ... -
Understand the Serializable and serialVersionUID
2011-03-08 12:54 1537看了一篇简单介绍serialVersionUI ... -
Overriding and Hiding Methods
2011-03-05 16:18 1666方法的override体现在实例方法(inst ... -
Java的参数传递
2011-03-02 16:32 1230关于java参数传递这个问题,伴随着java的 ...
相关推荐
"InvocationHandler中invoke()方法的调用问题分析" InvocationHandler 中的 invoke() 方法是 Java 语言中动态代理的关键所在。通过分析 invoke() 方法的调用问题,可以更好地理解 Java 中的动态代理机制。 首先,...
`invoke`方法是Java动态代理中的核心方法,它位于`java.lang.reflect.InvocationHandler`接口中。本篇文章将深入探讨代理类中`invoke`方法的使用及其注意事项。 代理类主要用于实现AOP(面向切面编程),它可以提供...
当通过`Proxy`类生成的代理对象上的任何方法被调用时,实际的工作都是由`InvocationHandler`的`invoke`方法完成的。`invoke`方法接受三个参数:被代理的对象、被调用的方法以及方法调用时的参数。开发者可以在这个...
这种行为通常体现在调用代理对象的方法时,实际上会执行`InvocationHandler`中的`invoke`方法。 `InvocationHandler`接口只有一个方法: ```java Object invoke(Object proxy, Method method, Object[] args) ...
当通过代理对象调用方法时,实际执行的是`InvocationHandler`的`invoke`方法。`invoke`方法接收三个参数:代理对象、被调用的方法和方法调用的参数。开发者需要在实现`invoke`方法时编写增强逻辑。 ```java public ...
`Proxy`通常与`InvocationHandler`接口一起使用,`InvocationHandler`定义了一个方法`invoke()`,当代理对象的方法被调用时,`invoke()`会被触发。 `InvocationHandler`接口包含一个方法: ```java Object invoke...
当客户端调用代理对象的方法时,实际上触发的是`InvocationHandler`的`invoke`方法,从而将调用转发到远程服务器执行。 Socket通信是网络编程的基础,它提供了一种在不同计算机之间建立连接并交换数据的方式。在RMI...
7. **动态代理**:Java的`java.lang.reflect.Proxy`类和`InvocationHandler`接口用于创建动态代理,实现对方法调用的拦截和增强。 在描述中提到的`getMethod()`和`invoke()`方法是反射的核心组成部分。`getMethod()...
`InvocationHandler`接口的`invoke()`方法在代理对象的方法被调用时会被触发,我们可以在这里实现回调逻辑: ```java public class MyInvocationHandler implements InvocationHandler { private Object target; ...
3. **拦截方法调用**:当调用代理对象的方法时,实际执行的是InvocationHandler的invoke方法。在invoke方法中,Spring会根据配置的元数据决定如何织入通知,如前置通知、后置通知、异常通知等。 4. **执行目标方法**...
`InvocationHandler`接口定义了一个`invoke()`方法,当代理对象调用其方法时,实际会调用`invoke()`方法。这个方法接收三个参数:代理对象、被调用的方法以及方法调用的参数。开发者可以在`invoke()`方法中实现...
动态代理通过实现`java.lang.reflect.InvocationHandler`接口并重写`invoke`方法来创建代理对象。`InvocationHandler`在接收到方法调用时,可以执行额外的操作,然后调用原始对象的方法。这种方式避免了静态代理中...
1. **InvocationHandler接口**:该接口包含一个方法`invoke()`,当通过代理对象调用原对象的方法时,实际上会执行`invoke()`方法。`invoke()`接收三个参数:代理对象、被调用的方法以及方法调用时的参数。开发者需要...
InvocationHandler 是一个接口,它定义了 invoke 方法,该方法将被代理对象的方法调用转发到 InvocationHandler 的 invoke 方法中。在 invoke 方法中,我们可以实现我们自己的逻辑,然后返回结果。 在 Spring AOP ...
5. 通过代理对象调用方法时,实际上会触发InvocationHandler的invoke方法,执行切面逻辑和目标方法。 动态代理的优点在于其灵活性和便捷性,可以在运行时动态地创建和修改代理对象,无需修改原有代码。然而,它也有...
在这个方法中,Spring首先进行一些特殊处理,例如检查当前调用的方法是否是`equals()`或`hashCode()`,如果是,则直接返回相应的结果,以避免代理对象与目标对象的等价性判断出现问题。 接下来,Spring检查调用的...
4. 通过代理对象调用方法,实际执行逻辑将在`InvocationHandler.invoke()`中进行。 下面是一个简单的动态代理示例: ```java interface MyService { void doSomething(); } class MyServiceImpl implements ...
5. 调用代理对象的方法:代理对象的方法调用会被转发给`InvocationHandler`的`invoke()`方法。 动态代理在Java中广泛应用,特别是在服务框架、ORM框架等中,它为我们提供了在不修改原有业务代码的情况下,对方法...
- 在`InvocationHandler.invoke()`中检查被调用方法是否带有这个注解。如果带有注解,那么在调用`method.invoke(target, args)`之前和之后,生成更详细的日志信息,比如方法执行前后的状态、时间消耗等。 4. **...