代理模式

声明 本文主要以图片的形式进行说明,具体的代码可自行去文末的链接处下载。


代理模式概念

  当一个对象(代理对象)以替身或占位符的形式,来控制程序对另一个对象(源对象)的访问时,就叫代理模式
在这里插入图片描述
在这里插入图片描述
注:代理对象与被代理对象(即源对象)之间的交互方式有很多,常见得有:组合(代理对象持有源对象)继承(代理对象继承源对象)远程调用(代理对象远程调用源对象)等等。

注:分类标准不同,分类结果也就不同。如,按照功效分,有保护代理虚拟代理等;按照实现分,有静态代理动态代理(JDK动态代理、CGLIB动态代理);按照代理对象被代理对象之间的关系分,有组合代理继承代理远程代理等等。

下面,介绍最常用的三种代理,以辅助理解代理模式

静态代理

  在笔者看来,一切硬编码的代理,都属于静态代理,包括又不限于下面这种通过实现相同接口、一个子类(代理类)持有另一个子类(被代理类)的实现方式

  • 编写静态代理
    在这里插入图片描述
  • 测试一下
    • 编写测试类:
      在这里插入图片描述
    • 运行main方法,程序输出:
      在这里插入图片描述

JDK动态代理

  JDK动态代理,是利用java.lang.reflect.Proxy#newProxyInstance方法并基于(被代理类所直接实现的)接口进行的代理类的生成。Proxy#newProxyInstance内部其实是调用sun.misc.ProxyGenerator#generateProxyClass方法直接生成的字节码代理类。所以,如果我们需要观察JDK动态代理生成的类长什么样子的话,直接使用sun.misc.ProxyGenerator#generateProxyClass输出class文件(代理类)是一种不错的选择。

注:实际上,哪怕一个类没有实现任何接口,这个类也是也可通过Proxy#newProxyInstance生成一个新的对象的,不过这个对象仅能作为一个基本的Object来使用,在试图将其强转为任何其它类(或当作某个具体类对象来使用)时,会报错,这是没有意义的;所以我们一般都是对那些有实现接口的类进行JDK动态代理。

  • 使用示例

    • 第一步: 准备接口及其实现。
      在这里插入图片描述
    • 第二步: 编写InvocationHandler。
      在这里插入图片描述
    • 第三步: 使用测试。
      • 编写测试类,并运行主函数:
        在这里插入图片描述
      • 观察控制台输出结果:
        在这里插入图片描述
  • 观察生成的JDK动态代理类
    在这里插入图片描述

CGLIB动态代理

  JDK动态代理基于Method#invoke,为了保证通用性,Method#invoke时,传参类型是Object[],在invoke方法内部,有对参数Object[]的校验、转换等逻辑。这就使得每次调用invoke都会走一遍上述逻辑,在系统调用量很大时,这无疑是非常消耗性能的
  CGLIB通过辅助类FastClass来进行方法的定位,在为被代理对象生成CGLIB代理对象时,会同时为这个CGLIB代理对象生成一个FastClass辅助类。生成这个辅助类的主要目的是:提供一种代理对象方法调用时,不走反射的解决方案。这是一种"笨办法",FastClass内会穷举出该代理可能会用到的所有方法,通过一个与方法签名挂钩的int值,来switch到具体的obj.xxx(...)。简单的说,FastClass内走的是对象打点调用方法,而不是Method#invoke
  所以,JDK动态代理与CGLIB动态代理的优劣是:JDK的实现相关简单、生成动态代理类快,但是运行时效率相对较低;CGLIB的实现相对复杂、生成动态代理类略慢(至于慢多少,以现在的软硬件设备来说,其实都可以忽略不计的),但是运行时效率相对较高

  • 使用示例

    • 第一步: 编写CGLIB的MethodInterceptor。
      在这里插入图片描述
    • 第二步: 使用测试。
      • 假设我们现在有类SayHey(用于一般测试)、Other1(用于测试cglib不会代理final方法):
        在这里插入图片描述
        在这里插入图片描述
      • 也编写了针对SayHey的测试类:
        在这里插入图片描述
      • 运行程序,控制台输出:
        在这里插入图片描述
  • 调试CGLIB,以学习CGLIB实现原理
    提示 这里主要分享如何进行CGLIB调试,其实现原理自己去调试自己去看。总的来说,会调试了,其实现原理自然就明白了。
    提示 若少侠嫌下述步骤麻烦,那么可以直接去将本人的项目拽下来,以本人提供的几个类进行debug调试。

    • 概述:
      在这里插入图片描述
    • 第一步: 使用cglib本地调试模式,获取到生成的CGLIB代理类、FastClass类。
      在这里插入图片描述
      运行主函数,对应位置就有了class文件:

    在这里插入图片描述

    • 第二步: 利用反编译工具(建议直接使用IDEA),反编译class文件,然后将内容拷贝至同名的class文件(无则创建)中,若有报错则自行灵活修改一下即可。
      在这里插入图片描述
      注:java文件放哪里自行决定即可。

    • 第三步(相对麻烦一点): 编写debug测试类、暴力修改器。
      在这里插入图片描述
      在这里插入图片描述

    • 第四步: 在第二步的三个类中的对应位置打断点,debug测试类,进行调试。
      在这里插入图片描述
      在这里插入图片描述


代理模式学习完毕 !

^_^ 如有不当之处,欢迎指正

^_^ 参考资料
        《Head First 设计模式》
Eric Freeman & Elisabeth Freeman with Kathy Sierra & Bert Bates著,O’Reilly Taiwan公司译,UMLChina改编

^_^ 测试代码托管链接
         https://github.com/JustryDeng…DesignPattern

^_^ 本文已经被收录进《程序员成长笔记》 ,笔者JustryDeng

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值