详解动态代理及其实现原理
1.什么是代理。比如(工厂,商店,客户),工厂是委托类,商店是代理类,工厂委托商店做代理来买产品,可以这样通俗理解。
2.代理的好处:(1.可以隐藏委托类的实现;2.可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理,比如买烟的时候限制年龄…可以在代理类中进行对应的操作。)
3.再讲动态代理之前,先明白静态代理是怎么实现的,若代理类在程序运行前就已经存在,那么这种代理方式被成为静态代理。
看代码:
公共的接口Sell:
package proxy;
/*** @author yhl* 解释为什么要都实现统一接口:通常情况下,静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类,不然之间没有一点关系,更谈不上代理什么了)*/
public interface Sell {void sell();//买东西
}
工厂类:
package proxy;public class Factory implements Sell{@Overridepublic void sell() {System.out.println("Factory sell..."); }}
Store类:
package proxy;public class Store implements Sell {//静态代理可以通过聚合来实现,让代理类持有一个委托类的引用即可。private Factory factory = null;public Store(Factory fc) {this.factory = fc;}@Overridepublic void sell() {System.out.println("before"); factory.sell();System.out.println("end"); }//重载一个方法public void sell(String param) {if(choose(param)){System.out.println("before"); factory.sell();System.out.println("end"); }else{System.out.println("不符合要求");}}/** 根据需求添加一个过滤功能---卖烟给学生* 通过静态代理,我们无需修改Factory类的代码就可以实现,只需在Store类中的sell方法中添加一个判断即可如下所示:*/public boolean choose(String param) {boolean flag = false;if (!param.equals("学生")) {flag = true;}return flag;}}
测试类:
package proxy;public class Client {@Testpublic void 静态代理测试() {Store st = new Store(new Factory());st.sell();st.sell("学生2");}}
代码看完之后:
总结静态类:照应第二个优点,可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。静态代理的局限在于运行前必须编写好代理类。
动态代理:
假设需求:在执行委托类中的方法之前输出“before”,在执行完毕后输出“after”。我们还是以上面例子中的Factory类作为委托类,Store类作为代理类来进行介绍,假如使用静态代理来实现这一需求,请返回看上边的store类代码,假如说委托对象里边有很多方法,那么这样的需求添加操作就显得不合适,造成代码的臃肿,显然不符合现在的代码编写规范。由此就引出动态代理。
基本概念:在程序运行时,根据委托类及其实现的接口,动态的创建一个代理类,当调用代理类实现的抽象方法时,就发起对委托类同样方法的调用(可以讲解完代码回头再看一遍)。
涉及的技术点:1.要提供一个实现了InvocationHandler接口的实现类,并重写其invoke()方法.2.通过Proxy获取动态代理实例,查看jdk-api。
先看一个完整的动态代理例子,看到的朋友可以直接拷贝在本地运行:
package proxy.dynmic;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;//接口
interface Human{void info();void fly();
}//委托类
class SuperMan implements Human{public void info(){System.out.println("我是超人"); }public void fly(){System.out.println("i can fly !!!");}
}class HumanUtil{public void method1(){System.out.println("执行method1");}public void method2(){System.out.println("执行method2");}
}//动态的创建一个代理类对象
class MyProxy{public static Object getProInstance(Object obj){MyInvocationHandler handle = new MyInvocationHandler();handle.setObject(obj);//将被代理对象传进去进行实例化(委托类)//创建一个obj对象对应的代理类return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),handle);}
}/*** handle* 处理器-中介类,实现了InvocationHandler这个接口的中介类用做“调用处理器”,”拦截“对代理类方法的调用。当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,* 代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数,这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用*/
class MyInvocationHandler implements InvocationHandler{Object obj = null;//被代理对象的声明(委托类)//实例化对象public void setObject(Object o){this.obj=o;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {//proxy参数传递的即是代理类的实例System.out.println("name:"+proxy.getClass().getName());//HumanUtil hu= new HumanUtil();//想要在执行1和2之间动态的执行插入的方法//hu.method1();Object returnval = method.invoke(obj, args);//hu.method2();return returnval;}}public class TestAop {public static void main(String[] args) {SuperMan sm = new SuperMan();Object obj = MyProxy.getProInstance(sm);//代理类对象,同样的代理类对象也是实现SuperMan接口的Human hm = (Human) obj;hm.info();//通过代理类的对象调用重写的抽象方法,实际上会转到invoke方法调用}
}
接下来,开始挨着解决疑点:
类 Proxy,提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。我们调用Proxy类的newProxyInstance方法来获取一个代理类实例。这个代理类实现了我们指定的接口并且会把方法调用分发到指定的调用处理器。
//动态的创建一个代理类对象
class MyProxy{public static Object getProInstance(Object obj){MyInvocationHandler handle = new MyInvocationHandler();handle.setObject(obj);//将被代理对象传进去进行实例化(委托类)//创建一个obj对象对应的代理类return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),handle);}
}
方法的三个参数含义分别如下:
loader:定义了代理类的ClassLoder;
interfaces:代理类实现的接口列表
h:调用处理器,也就是我们上面定义的实现了InvocationHandler接口的类实例。
对应的代码就是我们MyProxy类中的Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),handle);
handle就是调用处理器,指的是MyInvocationHandler这个类。
类InvocationHandler是代理实例的—调用处理程序(handler)—实现的接口。
每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
更加通俗的解释:在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,这个中介类被要求实现InvocationHandler接口,中介类其实就是调用处理程序。接下来我们来看看MyInvocationHandler这个中介类的类是怎么实现的:
/*** handle* 处理器-中介类,实现了InvocationHandler这个接口的中介类用做“调用处理器”,”拦截“对代理类方法的调用。当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,* 代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数,这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用*/
class MyInvocationHandler implements InvocationHandler{Object obj = null;//被代理对象的声明(委托类)//实例化对象public void setObject(Object o){this.obj=o;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {//proxy参数传递的即是代理类的实例System.out.println("name:"+proxy.getClass().getName());//HumanUtil hu= new HumanUtil();//想要在执行代理方法之前之后动态的插入1和2方法//hu.method1();Object returnval = method.invoke(obj, args);//hu.method2();return returnval;}}
最后再解释一下:
从以上代码中我们可以看到,中介类持有一个委托类对象引用,在invoke方法中调用了委托类对象的相应方法,看到这里是不是觉得似曾相识?通过聚合方式持有委托类对象引用,把外部对invoke的调用最终都转为对委托类对象的调用。这不就是我们上面介绍的静态代理的一种实现方式吗?实际上,中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类; 代理类与中介类也构成一个静态代理关系,在这个关系中,中介类是委托类,代理类是代理类。也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理。
至此,代理的模式已经给大家讲解完了,但是在此提出一个疑问,为什么在测试类中执行hm.info()时,实际上会转到invoke方法调用。当你执行完测试代码后会看到:
如图片所示,代理类的父类是Proxy,实现了Human接口,但是具体的代理类是怎么生成的,从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的地方,获得与指定类装载器和一组接口相关的代理类类型对象 Class cl = getProxyClass(loader, interfaces); /* * Invoke its constructor with the designated invocation handler. */ try { /* * Proxy源码开始有这样的定义: * private final static Class[] constructorParams = { InvocationHandler.class }; * cons即是形参为InvocationHandler类型的构造方法 */ // 调用代理对象的构造方法(也就是$Proxy0(InvocationHandler h))Constructor cons = cl.getConstructor(constructorParams); // 生成代理类的实例并把MyInvocationHandler的实例传给它的构造方法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()); }
}
看代码,newProxyInstance方法做了以下几件事. (1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类Proxy0,Proxy0类实现了interfaces的接口,并继承了Proxy类.
(2)实例化Proxy0并在构造方法中把委托类(handler–MyInvocationHandler)传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下:
Proxy0构造器:
public $Proxy0(InvocationHandler invocationhandler) { super(invocationhandler); }
父类Proxy构造器:
class Proxy{ InvocationHandler h=null; protected Proxy(InvocationHandler h) { this.h = h; } ...
}
看下生成的动态代理类(类Proxy的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据):
public final class $Proxy0 extends Proxy implements Human{ 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("***.SuperMan").getMethod("info", 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 info() { try { // 实际上就是调用MyInvocationHandler的public Object invoke(Object proxy, Method method, Object[] args)方法,第二个问题就解决了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); } }
}
总结
一个典型的动态代理创建对象过程可分为以下四个步骤:
1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
5、重点解决疑惑:
生成的$Proxy0继承Proxy类实现Human接口,实现的Human的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))
最后:
写到这里,基本上关于动态代理的相关问题就基本解释完成了,这篇文档的编写,也参考了不少相关文档,自己也看了一些视频介绍,自己会在下边列出参考文献,如果有读到这篇文章的朋友们,要是还有疑问的话,可以留言一起探讨,共同进步!!!如果想要再深一层次的去理解,就要熟读源码了,这个随着以后能力的提升再次拜读。。。相关切面Aop我在这里就不再多说了,理解起来也比较简单,可以再参考一些其他的,就到这吧。
参考文献:
http://blog.csdn.net/zcc_0015/article/details/22695647
http://rejoy.iteye.com/blog/1627405?page=2
https://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html
详解动态代理及其实现原理相关推荐
- java详解动态代理中的代理对象
相信大家都使用过动态代理,就算没有写过,应该也用过Spring来做过Bean的组织管理.如果使用过Spring,那大多数情况应该已经不知不觉地用到动态代理了. 动态代理中所说的"动态&quo ...
- 太阳动态ip代理为您详解使用代理ip常见的几个关键点及其解决方案
一个ip支撑不了整个爬虫项目的运营,因此程序员常需要建立代理ip池或者与第三方代理ip软件打交道.作为优质的代理ip服务商,太阳动态ip代理为您详解使用代理ip常见的几个关键点及其解决方案. IP检验 ...
- LwIP 之五 详解动态内存管理 内存堆(mem.c/h)
写在前面 目前网上有很多介绍LwIP内存的文章,但是绝大多数都不够详细,甚至很多介绍都是错误的!无论是代码的说明还是给出的图例,都欠佳!下面就从源代码,到图例详细进行说明. 目前,网络上多数文 ...
- 计算机网络知识详解之:TCP连接原理详解
网络知识详解之:TCP连接原理详解 计算机网络相关知识体系详解 网络知识详解之:TCP连接原理详解 网络知识详解之:HTTP协议基础 网络知识详解之:HTTPS通信原理剖析(对称.非对称加密.数字签名 ...
- 详解音频编解码的原理、演进和应用选型等
本文来自网易云音乐音视频实验室负责人刘华平在LiveVideoStackCon 2017大会上的分享,并由LiveVideoStack根据演讲内容整理而成(本次演讲PPT文稿,请从文末附件下载). 1 ...
- 即时通讯音视频开发(十八):详解音频编解码的原理、演进和应用选型
1.引言 大家好,我是刘华平,从毕业到现在我一直在从事音视频领域相关工作,也有一些自己的创业项目,曾为早期Google Android SDK多媒体架构的构建作出贡献. 就音频而言,无论是算法多样性, ...
- 【干货】JDK动态代理的实现原理以及如何手写一个JDK动态代理
动态代理 代理模式是设计模式中非常重要的一种类型,而设计模式又是编程中非常重要的知识点,特别是在业务系统的重构中,更是有举足轻重的地位.代理模式从类型上来说,可以分为静态代理和动态代理两种类型. 在解 ...
- [深入浅出Cocoa]之消息(二)-详解动态方法决议(Dynamic Method Resolution)
[深入浅出Cocoa]之消息(二)-详解动态方法决议(Dynamic Method Resolution) 罗朝辉 (http://www.cnblogs.com/kesalin/) 本文遵循&quo ...
- LwIP 之六 详解动态内存管理 内存池(memp.c/h)
该文主要是接上一部分LwIP 之 详解动态内存管理 内存堆(mem.c/h),该部分许多内容需要用到上一篇的内容.该部分主要是详细介绍LwIP中的动态内存池.整个内存池的实现相较于内存堆来说,还是 ...
- 高级JAVA - 动态代理的实现原理和源码分析
在之前的一篇文章中 , 我们简单了解了一下代理模式(JAVA设计模式 - 代理模式) , 本篇我们来学习一下动态代理的实现原理 , 以及源码是怎样的 . JDK动态代理的主要实现步骤如下 : 1 . ...
最新文章
- python学多久可以接单-零基础小白多久能学会python
- MySQL的诡异同步问题-重复执行一条relay-log
- 环境在c盘_如何给女朋友解释为什么 Windows 上面的软件都把自己安装在 C 盘
- Log4j 第三次发布漏洞补丁,漏洞或将长存
- 李航:做本质的、严谨的、有意思的研究,纪念我的导师长尾真教授
- linux查看进程加载的jar包,[Linux] 查看jar包内容
- 项目总结报告(联东U谷)
- cocos2dx基础篇(14)——基本绘图DrawPrimitives
- mysql sql语句遇到错误继续_MySQL从数据库sql语句执行错误解决方法
- 速达3000pro saas数据库修复
- VirtualBox成功安装Ubuntu18.04设置共享文件夹总结
- 【翻译】Javac骇客指南
- 伟大的数学家,怎么都诞生在法国?
- HTTP 错误 500.19 代码 0x8007000d 解决方案 for win7_64 asp.net core IIS Web Core
- 适合运动时戴的蓝牙耳机有哪些、非常优秀的运动型蓝牙耳机推荐
- 微软企业库(Microsoft Enterprise Library Data Access Block)
- 支持微信多开、防止消息撤回的小助手
- 个体工商户怎么开通微信支付功能及收款码?
- kaggle之Dogs vs. Cats(Keras)
- 《人工智能及其应用》课程笔记(四)第4章 非经典推理
热门文章
- 对HTTP异步接口进行性能测试
- 图片和文本置顶显示的方法
- 阵列天线方向图-均匀圆形/圆柱阵列matlab仿真
- doc 问卷调查模板表_调查问卷模板.doc
- xrd计算晶面间距_xrd如何计算晶格间距(1)
- 编写程序模拟用户输入手机号码、邮箱号码、座机号码,验证格式是否正确并给出提示,直到格式输入正确为止
- 【论文笔记】使用物理原理和领域知识进行无标注的监督学习
- html中css鼠标经过事件,css中鼠标点击变色 css里鼠标悬停变色怎么弄
- 树莓派教程 - 2.1 树莓派USB摄像头 树莓派罗技免驱摄像头 fswebcam常用参数
- 后端开发之如何写接口设计文档