共计 1646 个字符,预计需要花费 5 分钟才能阅读完成。
本文首发于泊浮目的专栏:https://segmentfault.com/blog…
前言
代理模式是在编程中非常常见的设计模式. 笔者在面试的过程中也经常会问到相关的问题, 但是很多同学答的并不尽人意. 在这篇文章中, 笔者想和大家聊聊代理模式的应用及一些实践.
What
先来一张图
我们可以很明显的看到, 代理和客户端发生了耦合, 而目标端则与客户端解耦.
Why
上文提到了一点, 松耦合. 而在任何设计模式中, 他们的目的都在以下范围内:
- 减少代码冗余度, 提高代码复用性
- 松耦合
这里提到了代码的复用性, 也可以多嘴一句, 代理模式可以帮助我们实现The Open Closed Principle
.
在这里, 我们可以举一个例子.Target 可能是一位不错的程序员,client 是一家公司. 在整个招聘流程中, 如果 Proxy 是猎头, 有些猎头则可能会想办法帮程序员提高身价. 而如果 Proxy 是 Hr, 则可能会来杀杀价. 而程序员走的流程可能一直是一样的:
- 电面
- 到面
- 签合同
我们可以把不同的行为 (讨价还价的特殊技巧) 写在不同的 Proxy 里(HrProxy or 猎头 Proxy), 而我们的程序员只要专心走流程就行了.
How
以 Java 中最常用的框架——Spring 为例.Spring 最主要提供了 2 个功能:
- IOC(Inversion of Control)
- AOP(Aspect Oriented Programming)
而我们知道,Spring 的 AOP 本质上是通过代理模式来做的. 接下来我们来详细聊聊 Spring 提供的 4 种类型的 AOP 支持:
- 基于代理的经典 Spring AOP;
- 纯 POJO 切面;
- @AspectJ 注解驱动的切面;
- 注入式 AspectJ 切面(适用于 Spring 各版本)。
前三种都是 Spring AOP 实现的变体,Spring AOP 构建在动态代理基础之上,因此,Spring 对 AOP 的支持局限于方法拦截。
而 SpringAOP 支持两种模式的 动态代理,JDK Proxy 和 cglib. 当 Spring 发现目标被代理类实现就接口时, 则用 JDK Proxy 来实现.
- JDK Proxy 不完全通过反射来做, 也有 ASM 进行字节码操作的. 本质是通过接口约定来做的
- cglib 完全通过 ASM 字节码来做. 本质通过继承的方式实现
代码大概长这样:
//spring aop 生成的代理
public class SpringAopTargetProxy extends Target{public void operate(){
//spring aop method1...
super.operate();
//spring aop method2...
}
}
而 AspectJ 是通过编译时编织来做的, 即在编译时插代码进去. 所以可以认为它基于 静态代理 来做 AOP.
基于以上, 我们也可以推导出 SpringAOP 对于 final
orstatic
方法是无效的.
call
和execution
有什么区别呢?
- call 就是在调用这个方法的地方插入代码
- execution 就是在调用这个方法的前面插入代码
代理模式的变化形式
之前, 我们根据代理生成的时机来区分了静态代理和动态代理. 而根据使用方式, 常见则有两类:
- Virtual Proxy: 只有当真正需要实例时, 它才生成和初始化实例
- Remote Proxy: 远程代理可以让我们不必关心 RealSubject 角色是否在网络上, 而是像调本地方法一样调用它的方法.Java 的 RMI(Remote Method Invocation)就相当于远程代理.
类似的设计模式
Adapter
Adapter 模式适配了两种具有不同接口 (API) 的对象, 以使它们可以一同工作。而在 Proxy 模式中, Proxy 角色与 RealSubject 角色的接口 (API) 是相同的(透明性)。
Decorator
Decorator 模式与 Proxy 模式在实现上很相似(比如 API 的一致性), 不过它们的使用目的不同——Decorator 模式的目的在于增加新的功能。而在 Proxy 模式中, 与增加新功能相比, 它更注重通过设置代理人的方式来减轻本人的工作负担.