设计模式中有一种模式叫代理模式,Spring框架离不开动态代理技术,Android hook技术用到了反射 + 动态代理,Framework中我们也经常看到各种proxy,如ApplicationThreadProxy, ActivityManagerProxy。

那么,今天就来说下Java中的代理模式和动态代理。

目录:

  1. 代理模式
  2. 静态代理
  3. 动态代理
  4. 代理模式的优缺点
  5. 代理模式的使用场景
  6. 动态代理原理分析

1. 代理模式

代理模式是常用的java设计模式,它的特征是代理类与委托类有相同的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性可以附加多种用途。

举一个通俗点的例子:

我们男生都爱看NBA,比如詹姆斯转会湖人,球队老板不会直接找詹姆斯,而是找他的经纪人商谈,这中间就拦了一道。詹姆斯可以打球,代言,他的经纪人也具有他一样的属性:打球,代言,但是真正打球,代言的是詹姆斯本人。

画一个草图理解下:

代理模式根据创建代理类的时间点,又可以分为静态代理和动态代理。

2. 静态代理

  • 2.1 概念

由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

  • 2.2 例子
private interface IProxy {void playBasketball();void endorsement();}private static final class James implements IProxy {@Overridepublic void playBasketball() {System.out.println("James play basketball");}@Overridepublic void endorsement() {System.out.println("James endorsement");}}private static final class Middleman implements IProxy {final James james;Middleman(James james) {this.james = james;}@Overridepublic void playBasketball() {System.out.println("Let's talk about the money, let James play basketball");james.playBasketball();}@Overridepublic void endorsement() {System.out.println("Let's talk about the money, let James endorsement");james.playBasketball();}}public static void main(String[] args) {Middleman middleman = new Middleman(new James());middleman.playBasketball();middleman.endorsement();}

执行输出:

Let's talk about the money, let James play basketball
James play basketball
Let's talk about the money, let James endorsement
James play basketball

3. 动态代理

  • 3.1 概念

代理类在程序运行时创建的代理被成为动态代理。 我们上面静态代理的例子中,代理类(Middleman)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的"指示"动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。

  • 3.2 "java.lang.reflect.Proxy"类

现在要生成某一个对象的代理对象,这个代理对象通常也要编写一个类来生成,所以首先要编写用于生成代理对象的类。在java中如何用程序去生成一个对象的代理对象呢,java在JDK1.5之后提供了一个"java.lang.reflect.Proxy"类,通过"Proxy"类提供的一个newProxyInstance方法用来创建一个对象的代理对象,如下所示:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

newProxyInstance()方法用来返回一个代理对象,这个方法总共有3个参数,ClassLoader loader用来指明生成代理对象使用哪个类装载器,Class<?>[] interfaces用来指明生成哪个对象的代理对象,通过接口指定,InvocationHandler h用来指明产生的这个代理对象要做什么事情。所以我们只需要调用newProxyInstance方法就可以得到某一个对象的代理对象了。

  • 3.3 例子
    private interface IProxy {void playBasketball();void endorsement();}private static final class James implements IProxy {@Overridepublic void playBasketball() {System.out.println("James play basketball");}@Overridepublic void endorsement() {System.out.println("James endorsement");}}private static final class MiddlemanProxy {private IProxy james = new James();IProxy getProxy() {return (IProxy) Proxy.newProxyInstance(James.class.getClassLoader(), james.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (method.getName().equals("playBasketball")) {System.out.println("Let's talk about the money, let James play basketball");return method.invoke(james, args);} else if (method.getName().equals("endorsement")) {System.out.println("Let's talk about the money, let James endorsement");return method.invoke(james, args);}return null;}});}}public static void main(String[] args) {MiddlemanProxy middlemanProxy = new MiddlemanProxy();IProxy proxy = middlemanProxy.getProxy();proxy.playBasketball();proxy.endorsement();}

执行输出:

Let's talk about the money, let James play basketball
James play basketball
Let's talk about the money, let James endorsement
James endorsement

可以看到,效果与静态代理一样,后面将分析动态代理的实现原理。

4. 代理模式的优缺点

优点:

  • 代理模式可以将代理对象和真实被调用的目标对象隔离。
  • 一定程度上降低了系统耦合度,扩展性好。
  • 保护目标对象。
  • 增强目标对象。

缺点:

  • 代理模式会造成系统设计中类的数量增加。
  • 在客户端和目标对象中间增加一个代理对象,会造成请求处理速度变慢。
  • 增加系统的复杂度。

5. 代理模式的使用场景

  • 1.远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是在本机器中,也可是在另一台机器中。远程代理又叫做大使(Ambassador)。好处是系统可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的对象是局域的而不是远程的,而代理对象承担了大部份的网络通讯工作。由于客户可能没有意识到会启动一个耗费时间的远程调用,因此客户没有必要的思想准备。
  • 2.虚拟(Virtual)代理:懒加载,根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。使用虚拟代理模式的好处就是代理对象可以在必要的时候才将被代理的对象加载,代理可以对加载的过程加以必要的优化。当一个模块的加载十分耗费资源的情况下,虚拟代理的好处就非常明显。
  • 3.Copy-on-Write代理:虚拟代理的一种。把复制(克隆)拖延到只有在客户端需要时,才真正采取行动。
  • 4.保护(Protect or Access)代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。保护代理的好处是它可以在运行时间对用户的有关权限进行检查,然后在核实后决定将调用传递给被代理的对象。
  • 5.Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
  • 6.防火墙(Firewall)代理:保护目标,不让恶意用户接近。
  • 7.同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
  • 8.智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。

6. 动态代理原理分析

以Proxy.newProxyInstance()作为入口分析。

@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{// InvocationHandler不能为空Objects.requireNonNull(h);// 将代理接口clone生成intfsfinal Class<?>[] intfs = interfaces.clone();// 获取系统的安全管理类final SecurityManager sm = System.getSecurityManager();if (sm != null) {// 检查访问权限checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}// 关键的一句,获取代理类的Class对象Class<?> cl = getProxyClass0(loader, intfs);try {if (sm != null) {// 校验权限checkNewProxyPermission(Reflection.getCallerClass(), cl);}// 获取代理类的构造器final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});}// 生成代理对象return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}

可以看到,利用了反射的机制,关键一句:Class<?> cl = getProxyClass0(loader, intfs)

private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}return proxyClassCache.get(loader, interfaces);}

这里产生了代理类,后面代码中的构造器也是通过这里产生的类来获得,可以看出这个类的产生就是整个动态代理的关键,由于是动态生成的类文件,这里不具体进入分析如何产生的这个类文件,只需要知道这个类文件是缓存在java虚拟机中的。

我们可以来看看系统生成的代理类class是什么样的:

   public static void main(String[] args) {MiddlemanProxy middlemanProxy = new MiddlemanProxy();IProxy proxy = middlemanProxy.getProxy();proxy.playBasketball();proxy.endorsement();byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", James.class.getInterfaces());FileOutputStream fos = null;try {fos = new FileOutputStream(new File("Proxy0.class"));fos.write(classFile);fos.flush();} catch (Exception e) {System.out.println(e.getMessage());} finally {if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}}}

看看生成的Proxy0.class:

public final class $Proxy0 extends Proxy implements IProxy {private static Method m1;private static Method m4;private static Method m2;private static Method m3;private static Method m0;public $Proxy0(InvocationHandler var1) throws  {super(var1);}public final boolean equals(Object var1) throws  {try {return (Boolean)super.h.invoke(this, m1, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final void endorsement() throws  {try {super.h.invoke(this, m4, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final String toString() throws  {try {return (String)super.h.invoke(this, m2, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final void playBasketball() throws  {try {super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final int hashCode() throws  {try {return (Integer)super.h.invoke(this, m0, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m4 = Class.forName("io.kzw.advance.csdn_blog.TestDynamicProxy$IProxy").getMethod("endorsement");m2 = Class.forName("java.lang.Object").getMethod("toString");m3 = Class.forName("io.kzw.advance.csdn_blog.TestDynamicProxy$IProxy").getMethod("playBasketball");m0 = Class.forName("java.lang.Object").getMethod("hashCode");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}

可以看到上面JDK生成的代理类class,和静态代理类的结构差不多,只不过调用目标对象是通过反射的方式。

Java篇 - 代理模式和动态代理实现原理相关推荐

  1. Java内功修炼系列:代理模式及动态代理

    目录 一 代理模式 1.1 简介 1.2 代理模式角色定义 二 静态代理 2.1 介绍和实例 2.2 静态代理的缺点 三 动态代理 3.1 基于JDK原生动态代理实现 四 小结 一 代理模式 1.1 ...

  2. Java拾遗:007 - 代理模式与动态代理

    2019独角兽企业重金招聘Python工程师标准>>> 代理模式 在日常开发中我们可以会接手一些老的项目,有时连源码都没有,或者有时候我会需要对业务逻辑做一定增强(功能扩展,如:日志 ...

  3. 代理模式、动态代理和面向方面

    代理的意思很好理解,它借鉴了我们日常所用的代理的意思:就是本来该自己亲自去做的某件事,由于某种原因不能直接做,而只能请人代替你做,这个被你请来做事的人就是代理.比如过春节要回家,由于你要上班,没时间去 ...

  4. 代理模式和动态代理模式_代理模式介绍

    代理模式和动态代理模式 代表:被选中或当选为他人投票或代理的人– Merriam-Webster . 委托模式:在软件工程中,委托模式是面向对象编程中的一种设计模式,其中,一个对象而不是执行其陈述的任 ...

  5. 【设计模式】--- 装饰器模式、静态代理模式和动态代理模式

    文章目录 1 引子 2 业务场景介绍 3 静态代理模式 4 装饰器模式 5 动态代理模式 5.1 Proxy --- 具体的代理对象生成组件 5.2 InvocationHandler --- 封装被 ...

  6. 设计模式—代理模式以及动态代理的实现

    代理模式(Proxy Design Pattern)是为一个对象提供一个替身,以控制对这个对象的访问.即通过代理对象访问目标对象.被代理的对象可以是远程对象.创建开销大的对象或需要安全控制的对象. 一 ...

  7. 20171030_chr_proxy 代理模式(动态代理)

    代理模式(动态代理) /20171030_chr_proxy/src/nuc/sw/dynamic/proxy/Dog.java package nuc.sw.dynamic.proxy;public ...

  8. java动态代理_Java代理模式及动态代理详解

    Java的动态代理在实践中有着广泛的使用场景,比如最场景的Spring AOP.Java注解的获取.日志.用户鉴权等.本篇文章带大家了解一下代理模式.静态代理以及基于JDK原生动态代理. 代理模式 无 ...

  9. Java代理模式及动态代理详解

    本文转自:程序新视界公众号 Java的动态代理在实践中有着广泛的使用场景,比如最场景的Spring AOP.Java注解的获取.日志.用户鉴权等.本篇文章带大家了解一下代理模式.静态代理以及基于JDK ...

最新文章

  1. 华南理工大学计算机操作系统课程设计大作业银行家死锁避免算法模拟,2016春操作系统大作业银行家死锁避免算法模拟.doc...
  2. Bash 实例,第 2 部分
  3. 08.Eclipse下Ndk开发(使用fmod实现QQ变声功能)
  4. 面试中经常会问的智力题,来看看你会做几道
  5. 微型计算机在温室管理中的应用初探,文献综述-测控051-陈杰.doc
  6. java中 若干,Java中的随机数发生器。产生若干的复杂性
  7. java8 循环jsonarray_JSONArray 遍历方式
  8. java 判断是否包含中文_java判断字符串中是否包含中文并过滤中文
  9. android让图片旋转动画,利用RotateAnimation旋转图片的问题 - 移动平台 / Android
  10. 使用 Anthem.NET 的常见回调(Callback)处理方式小结
  11. this kernel requires an x86-64 CPU, but only detected an i686 CPU
  12. python字符串出栈方法_Python学习之路_day_04(字符串与列表的内置方法)
  13. 三线一控电动球阀、三线两控电动球阀、两线制断电开阀、两线制断电关阀四类电动球阀的区别
  14. 详解CAN总线:CAN协议分层结构及功能
  15. Android 微信授权登陆
  16. 奇异值分解(SVD)
  17. C++ 模板中的类型获取(一)
  18. java计算机毕业设计冠军体育用品购物网站MyBatis+系统+LW文档+源码+调试部署
  19. win10防火墙推荐设置来保护计算机,大神详解win10隐藏防火墙和网络保护部分方法的完全解决要领...
  20. 一文总结经典卷积神经网络CNN模型

热门文章

  1. 计算机天才陈立杰:16岁拒上清华,婉拒谷歌,网瘾少年如何逆袭?
  2. 用VBA在Word文档中每页页眉插入返回文档目录中相应位置的超链接
  3. android studio文件存储路径,配置Android Studio的缓存文件路径(释放你的C盘)
  4. Vanilla Node.js REST API示例
  5. 消息通知系统详解2---后端设计
  6. java straem 过滤_Java Stream 过滤器
  7. 基于布谷鸟优化的BP神经网络(分类应用) - 附代码
  8. 5000万美金年薪!董事会重奖微软CEO
  9. 人工智能发展的核心——机器学习
  10. C语言程序设计(第四版,建议复习用,无死角)