一、什么是代理模式

代理模式(Proxy Design Pattern):代理模式是结构型模式,代理模式通过非侵入式的方式给原始类附加功能。

代理模式分为两种:静态代理动态代理

二、代理模式的作用

假设有如下代码:

[设计模式]-[结构型]-代理模式-介绍 - 图1

系统需要针对操作处理记录日志,上述代码通过侵入式的方式来实现了日志的记录功能,每一个需要记录日志的方法都需要增加相关代码。

不过也如代码所示,代码侵入性强,且功能性代码和业务代码高度耦合,后期如果日志操作变更,或者记录参数改动,或者需要进行更换日志框架,耦合性的代码会带来很大的问题。
这时候就可以使用代理模式来避免这些问题,通过代理模式可实现代码的无侵入日志功能。

三、静态代理

静态代理分为两种
1、被代理类实现了接口,代理类可以通过实现相同的接口,进行功能附加
2、被代理类没有实现接口,代理类可以通过继承被代理类,进行功能附加

3.1、和被代理类实现同一个接口实现代理

当被代理类实现了接口,代理类实现相同的接口进行功能增强,代码如下:

[设计模式]-[结构型]-代理模式-介绍 - 图2

上述代码主要三个类
**IOrderController** :API 统一接口定义
**OrderController** :实现了 IOrderController 并提供具体 API 实现
**ProxyOrderController** :实现了 IOrderController 同时持有 OrderController 实例对象,可以在 OrderController 无感知的情况下,增加相关功能

3.2、通过继承的方式实现代理

代码如下

[设计模式]-[结构型]-代理模式-介绍 - 图3

上述代码主要二个类
**OrderController** :提供具体 API 实现
**ProxyOrderController**:继承了 OrderController ,重写相关 API 实现,可以再调用父类方法前后进行功能增强。

静态代理的局限性

静态代理虽然能在无修改原有代码的基础上进行功能附加,但是需要实现重写或者继承被代理类,且实现的代理类只能对某个被代理类进行功能附加,如果有其他被代理类,需要在重新写一个新的代理类。

四、动态代理

动态代理同样存在两种方式:
1、接口方式的动态代理
2、继承方式的动态代理

4.1、接口方式的动态代理

接口方式的动态代理使用 JDK 自带的来实现
jdk 实现动态代理要点
1、代理对象需要实现 InvocationHandler 重写 invoke
2、被代理对象需要实现有接口

直接来看上述日志功能优化后,日志代理类 OperationLogProxy 代码,代码如下:

[设计模式]-[结构型]-代理模式-介绍 - 图4

该代理类能够代理任何符合标准(被代理类只要有实现接口)的被代理类,包括代理类中的所有方法。突破静态代理只能针对单一对象进行代理的局限性。

简单案例:通过上述日志代理类 **OperationLogProxy ** 进行代码增强

[设计模式]-[结构型]-代理模式-介绍 - 图5

jdk 动态代理的原理

在代码运行中,Jdk 会生成一个 **$Proxy** 开头的代理对象,例如下面

[设计模式]-[结构型]-代理模式-介绍 - 图6

下面通过反编译的方式来查看 JVM 生成的代理对象 **$Proxy0**
相关生成反编译代码的代码如下:

[设计模式]-[结构型]-代理模式-介绍 - 图7

生成后的 $Proxy 文件部分代码如下

[设计模式]-[结构型]-代理模式-介绍 - 图8

通过图片能够得出 jdk 动态代理的原理

JVM 通过字节码生成了一个新的类,该类继承了被代理对象的接口(如:IOrderController),同时持有了我们写的代理类(如:OperationLogProxy

字节码生成的类持有了代理类(如:OperationLogProxy)同时持有了 被代理类 (如:IOrderController) 的实例对象。

字节码生成的类重写了被代理类接口的所有方法,这些方法的调用都需要使用反射来调用,而调用的顺序在代理类(如:OperationLogProxy) 中的 invoke 方法。

再来回顾下 **OperationLogProxy#invoke**

[设计模式]-[结构型]-代理模式-介绍 - 图9

在反射执行调用前,我们可以在他的前后插入我们需要执行的逻辑,

这就是 jdk 动态代理的实现原理。

4.2、继承的方式实现动态代理

JDK 本身只提供了接口方式动态代理,继承方式的动态代理可以使用 cglib 来实现。

相关实现代码如下:

[设计模式]-[结构型]-代理模式-介绍 - 图10

单纯从类的数量来说,比较于 JDK 动态代理, cglib 动态代理 减少了接口类的定义。

cglib 动态代理原理

通过 Cglib 提供的测试工具,将cglib 字节码操作后的类存入到本地磁盘

部分代码如下:

[设计模式]-[结构型]-代理模式-介绍 - 图11

cglib 的原理

通过字节码生成一个新的类OrderControllerEnhancerByCGLIBfe4a213继承被代理类OrderController,同时持有代理类CglibOperationLogProxy (也就是 MethodInterceptor) 的实例对象。

方法调用的子类OrderControllerEnhancerByCGLIBfe4a213,然后调用的CglibOperationLogProxy (也就是 MethodInterceptor) , 最后调用的代理类OrderController
在调用链路中,优先调用的 CglibOperationLogProxy (也就是 MethodInterceptor) 中的 interceptor 方法,最后调用的 OrderController,
而代码的增强在 interceptor 方法中完成,从而实现代码无侵入性的增强。


【公众号】花好夜猿
wxlogo.jpg