设计模式之(8)——代理模式

定义:为某个对象提供一个代理,以达到对这个对象的访问控制,代理类和委托类有共同的父类或者父接口,这样可以在使用委托类的地方都可以使用代理对象来替换(这符合程序设计中的” 里氏替换原则“),代理类负责请求的预处理、过滤等初步处理之后,再将请求分派给委托类进行处理,代理类当然也可以在委托类执行完毕之后做一些其它工作;

代理模式根据代理类的生成时间不同可以静态代理和动态代理。

静态代理:是由程序员创建或工具生成代理类的源码,在编译期就已经确定了委托类和代理类,在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就已经确定了。

动态代理:动态代理类的源码是在程序运行期间由JVM根据反射等机制动态生成的 ,所以不存在代理类的字节码文件,代理类和委托类的关系在程序运行时动态确定。

说了这么多那么代理有什么优点呢?

说起了一大堆,总的来说主要有两大点:1、可以对客户端隐藏委托类的实现;2、可以实现客户端和委托类之间的解耦,在不修改委托类的情况下做一些其他的处理,当然核心业务还得调用委托类的方法处理;

日常生活中代理的场景很常见,比如说我们有一套房子需要出售,但是我们没有时间自己天天带看房,那么我们就可以把这套房子挂委托给房产中介,中介就可以帮我们筛选潜在客户,带客户看房,确定买家之后,中介就联系我们和买家签订合同,买家付款、双方完成过户、房屋物业水电燃气交接事宜等,在这个过程中卖家就是委托类,而中介就是代理类;

其实在Java中也有很多场景需要使用代理,如RPC的远程调用,我们就是通过代理类去实现的,还有Spring中的AOP切面也是为切面生成了代理类;

下面我们先讲讲静态代理的实现:

1、定义接口和接口的实现(委托类);

2、定义代理类(定义接口的代理对象);

3、将接口的实例注入到代理对象中,然后通过代理对象去调用委托类的实现;

静态代理的示例代码如下:

通过以上代码我们也不难发现静态代理的缺点也很明显:假设系统中有N个委托类需要代理,那么可能就需要N个代理类,这就容易造成系统的类爆炸,再者假如委托类中的方法很多,那么也可能在代理类中存在大量的重复代码,所以我们可以看出静态代理的可复用性不高。那么我们如何解决上面这个问题呢?答案就是动态代理。

动态代理分为两种一种是基于接口的jdk的动态代理,一种是基于继承的cglib的动态代理,我们先来说说jdk的动态代理。

一个JAVA类在JVM中的生命周期分为这几个过程:加载-》验证->准备-》解析-》初始化-》使用-》卸载,而其中的加载阶段主要完成以下三件事情:

1、通过一个类的全限定名来获取定义此类的二进制流;

2、将这个字节流所代表的静态数据结构转换为方法区的运行时数据结构;

3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区中这个类的各种数据的访问入口;

而我们要说的动态代理,主要就发生在第一个阶段, 这个阶段类的二进制字节流的来源可以有很多, 比如 zip 包、网络、运行时计算生成、其它文件生成 (JSP)、数据库获取。其中运行时计算生成就是我们所说的动态代理技术,在 Proxy 类中, 就是运用了 ProxyGenerator.generateProxyClass 来为特定接口生成形式为 * $Proxy 的代理类的二进制字节流。所谓的动态代理就是想办法根据接口或者目标对象计算出代理类的字节码然后加载进 JVM 中。实际计算的情况会很复杂,我们借助一些诸如 JDK 动态代理实现;

jdk的动态代理是在程序运行时,根据一组接口定义,使用Proxy、InvocationHandler等工具类生成代理对象的实例;

下面是jdk动态代理测试的demo:

在以上的测试代码中,我们调用Proxy类的newProxyInstance()方法来获取一个代理对象的实例,这个实例实现了我们指定的接口,并且会把方法调用分发到我们指定的调用处理器MyInvocationHandler中,调用invoke()方法,我们在invoke()方法中调用委托类的对应方法,并添加上自己的处理逻辑;

jdk的动态代理最大的特点是 代理类和委托类实现共同的接口,jdk的动态代理内部其实是通过反射机制来实现的,已知一个对象,在运行的时候动态调用它的方法,并且在调用的时候还可以家上一些自己的逻辑在里面;

那假如没有结构我们该如何实现动态代理呢?这时候我们的cglib动态代理就横空出世啦。

cglib的动态代理是通过一个第三方框架来实现的,所以我们在使用的时候应该引入对应的jar包,例如:

其原理大致是:对指定的委托类生成一个子类并重写其中的业务方法来实现的;

cglib的测试代码如下:

cglib动态代理的创建过程可以总结为以下几个步骤:

1、查找目标类定义的所有非final修饰的public类型的方法;

2、将符合条件的方法定义转换成字节码;

3、将字节码转换成相应的代理的class对象;

4、实现MethodInterceptor接口,用来处理对所有代理方法的拦截;

jdk动态代理和cglib动态代理的比较:

jdk的动态代理:基于反射来实现,委托类必须实现了接口才能创建代理类,代码实现简单,简化了开发和维护,jdk原生支持,反射速度较慢;

cglib的动态代理:基于ASM机制,通过字节码技术,通过生成委托类的子类,采用方法拦截的技术来拦截所有父类方法的调用,织入横切逻辑,完成代理,并且无需实现接口,达到代理类的无侵入,只关心业务类即可,并且是直接操作字节码生成的,速度上有一定的优势,但是无法对final修饰的方法进行代理,spring全家桶中的很多功能是通过这种方式来实现的;

注意事项:

1、和适配器模式相比,适配器模式重点在于改变所考虑对象的接口,而代理不能改变所代理对象的接口;

2、和装饰设计模式相比,装饰设计模式重点在于强调类功能的增强,而代理模式的重点在于对象的访问控制;

Original: https://www.cnblogs.com/wha6239/p/16650461.html
Author: 一只烤鸭朝北走
Title: 设计模式之(8)——代理模式

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/612890/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球