以下的内容部分参考了网络上的内容,在此对原作者表示感谢!

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

Java代码  
  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
  2. {
  3. method.invoke(obj, args);
  4. return null;
  5. }

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

Java代码  
  1. 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参数传递的即是代理类的实例。

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

Java代码  
  1. //抽象角色(动态代理只能代理接口)
  2. public interface Subject {
  3. public void request();
  4. }
Java代码  
  1. //真实角色:实现了Subject的request()方法
  2. public class RealSubject implements Subject{
  3. public void request(){
  4. System.out.println("From real subject.");
  5. }
  6. }
Java代码  
  1. //实现了InvocationHandler
  2. public class DynamicSubject implements InvocationHandler
  3. {
  4. private Object obj;//这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象
  5. public DynamicSubject()
  6. {
  7. }
  8. public DynamicSubject(Object obj)
  9. {
  10. this.obj = obj;
  11. }
  12. //这个方法不是我们显示的去调用
  13. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
  14. {
  15. System.out.println("before calling " + method);
  16. method.invoke(obj, args);
  17. System.out.println("after calling " + method);
  18. return null;
  19. }
  20. }
Java代码  
  1. //客户端:生成代理实例,并调用了request()方法
  2. public class Client {
  3. public static void main(String[] args) throws Throwable{
  4. // TODO Auto-generated method stub
  5. Subject rs=new RealSubject();//这里指定被代理类
  6. InvocationHandler ds=new DynamicSubject(rs);
  7. Class<?> cls=rs.getClass();
  8. //以下是一次性生成代理
  9. Subject subject=(Subject) Proxy.newProxyInstance(
  10. cls.getClassLoader(),cls.getInterfaces(), ds);
  11. //这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口
  12. System.out.println(subject instanceof Proxy);
  13. //这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口
  14. System.out.println("subject的Class类是:"+subject.getClass().toString());
  15. System.out.print("subject中的属性有:");
  16. Field[] field=subject.getClass().getDeclaredFields();
  17. for(Field f:field){
  18. System.out.print(f.getName()+", ");
  19. }
  20. System.out.print("\n"+"subject中的方法有:");
  21. Method[] method=subject.getClass().getDeclaredMethods();
  22. for(Method m:method){
  23. System.out.print(m.getName()+", ");
  24. }
  25. System.out.println("\n"+"subject的父类是:"+subject.getClass().getSuperclass());
  26. System.out.print("\n"+"subject实现的接口是:");
  27. Class<?>[] interfaces=subject.getClass().getInterfaces();
  28. for(Class<?> i:interfaces){
  29. System.out.print(i.getName()+", ");
  30. }
  31. System.out.println("\n\n"+"运行结果为:");
  32. subject.request();
  33. }
  34. }
Xml代码  
  1. 运行结果如下:此处省略了包名,***代替
  2. true
  3. subject的Class类是:class $Proxy0
  4. subject中的属性有:m1, m3, m0, m2,
  5. subject中的方法有:request, hashCode, equals, toString,
  6. subject的父类是:class java.lang.reflect.Proxy
  7. subject实现的接口是:cn.edu.ustc.dynamicproxy.Subject,
  8. 运行结果为:
  9. before calling public abstract void ***.Subject.request()
  10. From real subject.
  11. 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方法的源代码:

Java代码  
  1. public static Object newProxyInstance(ClassLoader loader,
  2. Class<?>[] interfaces,
  3. InvocationHandler h)
  4. throws IllegalArgumentException
  5. {
  6. if (h == null) {
  7. throw new NullPointerException();
  8. }
  9. /*
  10. * Look up or generate the designated proxy class.
  11. */
  12. Class cl = getProxyClass(loader, interfaces);
  13. /*
  14. * Invoke its constructor with the designated invocation handler.
  15. */
  16. try {
  17. /*
  18. * Proxy源码开始有这样的定义:
  19. * private final static Class[] constructorParams = { InvocationHandler.class };
  20. * cons即是形参为InvocationHandler类型的构造方法
  21. */
  22. Constructor cons = cl.getConstructor(constructorParams);
  23. return (Object) cons.newInstance(new Object[] { h });
  24. } catch (NoSuchMethodException e) {
  25. throw new InternalError(e.toString());
  26. } catch (IllegalAccessException e) {
  27. throw new InternalError(e.toString());
  28. } catch (InstantiationException e) {
  29. throw new InternalError(e.toString());
  30. } catch (InvocationTargetException e) {
  31. throw new InternalError(e.toString());
  32. }
  33. }

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

Java代码  
  1. class Proxy{
  2. InvocationHandler h=null;
  3. protected Proxy(InvocationHandler h) {
  4. this.h = h;
  5. }
  6. ...
  7. }

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

Java代码  
  1. public final class $Proxy0 extends Proxy implements Subject {
  2. private static Method m1;
  3. private static Method m0;
  4. private static Method m3;
  5. private static Method m2;
  6. static {
  7. try {
  8. m1 = Class.forName("java.lang.Object").getMethod("equals",
  9. new Class[] { Class.forName("java.lang.Object") });
  10. m0 = Class.forName("java.lang.Object").getMethod("hashCode",
  11. new Class[0]);
  12. m3 = Class.forName("***.RealSubject").getMethod("request",
  13. new Class[0]);
  14. m2 = Class.forName("java.lang.Object").getMethod("toString",
  15. new Class[0]);
  16. } catch (NoSuchMethodException nosuchmethodexception) {
  17. throw new NoSuchMethodError(nosuchmethodexception.getMessage());
  18. } catch (ClassNotFoundException classnotfoundexception) {
  19. throw new NoClassDefFoundError(classnotfoundexception.getMessage());
  20. }
  21. } //static
  22. public $Proxy0(InvocationHandler invocationhandler) {
  23. super(invocationhandler);
  24. }
  25. @Override
  26. public final boolean equals(Object obj) {
  27. try {
  28. return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
  29. } catch (Throwable throwable) {
  30. throw new UndeclaredThrowableException(throwable);
  31. }
  32. }
  33. @Override
  34. public final int hashCode() {
  35. try {
  36. return ((Integer) super.h.invoke(this, m0, null)).intValue();
  37. } catch (Throwable throwable) {
  38. throw new UndeclaredThrowableException(throwable);
  39. }
  40. }
  41. public final void request() {
  42. try {
  43. super.h.invoke(this, m3, null);
  44. return;
  45. } catch (Error e) {
  46. } catch (Throwable throwable) {
  47. throw new UndeclaredThrowableException(throwable);
  48. }
  49. }
  50. @Override
  51. public final String toString() {
  52. try {
  53. return (String) super.h.invoke(this, m2, null);
  54. } catch (Throwable throwable) {
  55. throw new UndeclaredThrowableException(throwable);
  56. }
  57. }
  58. }

接着把得到的$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中InvocationHandler 用于实现代理。

转载于:https://www.cnblogs.com/langtianya/p/5013034.html

java中InvocationHandler 用于实现代理。相关推荐

  1. java中为什么要使用代理

    引入代理: 我们为什么要引入java的代理,除了当前类能够提供的功能外,我们还需要补充一些其他功能. 最容易想到的情况就是权限过滤,我有一个类做某项业务,但是由于安全原因只有某些用户才可以调用这个类, ...

  2. 详解 Java 中的三种代理模式

    代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 这里使用 ...

  3. Java中的三种代理模式解释

    本文转自:Java技术栈 www.javastack.cn 代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的 ...

  4. java中可用于定义成员常量_13秋北航《Java语言与面向对象程序设计》在线作业三辅导 …...

    需要答案的请联系:qq或者微信 514169068 一. 惟一选项(共 15 道试题,共 60 分.)V 1.设有定义 float x=3.5f, y=4.6f, z=5.7f:则以下的表白式中,值为 ...

  5. Java中的设计模式:“代理模式”的理解

    代理模式定义: 为其他对象提供一种代理以控制对这个对象的访问.在面向对象中,有时候直接访问一些对象比较麻烦,所以代理模式就是在这个对象上加上一个访问该对象的访问层.类似于很多明星的事务实际都是交给经纪 ...

  6. Java中使用IE Proxy代理的方法

    在Java的网络编程中,有时候内网服务器需要访问外网的网络资源,这时候就需要使用代理. 完整的代码下载:src.rar 一般系统在ie浏览器中设置的代理,java无法访问到,可通过以下代码进行: st ...

  7. java中各种使用设置代理的方法

    1.http代理 package main.java.com.example; import org.apache.http.HttpHost; import org.apache.http.clie ...

  8. JAVA中经过nginx反向代理获取客户端ip并获取相关坐标等信息

    关于搜狐新浪ip库查询接口的使用 直接输出访客ip及所在城市: <script src="http://pv.sohu.com/cityjson?ie=utf-8" > ...

  9. java中实现代理及委托代理模式

    InvocationHandler 用于实现代理. 如果不用InvocationHandler接口实现代理的话,我们写代码是这样的: 定义一个接口: Java code interface Greet ...

最新文章

  1. 分布式监控系统Zabbix3.2对数据库的连接数预警
  2. 室外电磁赛道铺设补充说明
  3. 第三篇:稳定性之借风险之力驱动架构演进
  4. 缺钱的特斯拉,是否在走向“乐视化”?
  5. python学习系列--str类型
  6. c语言翻转棋ai算法,有没有人懂黑白棋(翻转棋)的核心算法
  7. cad缩放_CAD两种缩放技巧,你喜欢那个?
  8. win10开机桌面假死鼠标能动怎么办
  9. u-boot之ARM920T的start.S分析
  10. Android开发之中文语音朗读
  11. win7点e出现计算机,Win7电脑蓝屏显示代码0x0000007e的解决方法
  12. ios 简单实现半圆形仪表盘 (进度条)
  13. python b64encode_python base64编码解码、SHA256编码、urlsafe_b64encode编码
  14. 有关信息论和 error-control coding 的简单介绍
  15. java中UPD的一个简单的例子
  16. 颜色编码 RGB、CMYK、 HEX
  17. PG的管道模式如何工作
  18. 用python获取指定路径下的所有目录路径和文件路径
  19. java象棋实验报告_中国象棋java程序设计实验报告.doc
  20. Riak - 安装运维篇(1)

热门文章

  1. centos 6.4 postfix mysql_CentOS 6.4下Postfix邮件服务安装和基本配置
  2. 自然语言处理中的Attention Model原理介绍
  3. 帮肋营销人员建立策略思考能力
  4. 让元素固定_49种元素对钢铁性能的影响
  5. 2018-05-16树莓派如何开启UART串口
  6. 银行对公业务结构图梳理
  7. 如何让Toast响应点击事件等基础Android基础文章N篇
  8. 新一代MMO架构(Next Generation MMO Architecture 翻译)
  9. iOS TableView 使用详解
  10. 游戏开发经验分享:我所理解的打击感