代理模式 {#%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F}
亦称: Proxy
定义 {#%E5%AE%9A%E4%B9%89}
- 由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
意图 {#%E6%84%8F%E5%9B%BE}
- 代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
问题 {#%E9%97%AE%E9%A2%98}
为什么要控制对于某个对象的访问呢? 举个例子: 有这样一个消耗大量系统资源的巨型对象, 你只是偶尔需要使用它, 并非总是需要。
数据库查询有可能会非常缓慢。
-
你可以实现延迟初始化: 在实际有需要时再创建该对象。 对象的所有客户端都要执行延迟初始代码。 不幸的是, 这很可能会带来很多重复代码。
-
在理想情况下, 我们希望将代码直接放入对象的类中, 但这并非总是能实现: 比如类可能是第三方封闭库的一部分。
解决方案 {#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88}
代理模式建议新建一个与原服务对象接口相同的代理类, 然后更新应用以将代理对象传递给所有原始对象客户端。 代理类接收到客户端请求后会创建实际的服务对象, 并将所有工作委派给它。
代理将自己伪装成数据库对象, 可在客户端或实际数据库对象不知情的情况下处理延迟初始化和缓存查询结果的工作。
- 这有什么好处呢? 如果需要在类的主要业务逻辑前后执行一些工作, 你无需修改类就能完成这项工作。 由于代理实现的接口与原类相同, 因此你可将其传递给任何一个使用实际服务对象的客户端。
真实世界类比 {#%E7%9C%9F%E5%AE%9E%E4%B8%96%E7%95%8C%E7%B1%BB%E6%AF%94}
信用卡和现金在支付过程中的用处相同。
- 信用卡是银行账户的代理, 银行账户则是一大捆现金的代理。 它们都实现了同样的接口, 均可用于进行支付。 消费者会非常满意, 因为不必随身携带大量现金; 商店老板同样会十分高兴, 因为交易收入能以电子化的方式进入商店的银行账户中, 无需担心存款时出现现金丢失或被抢劫的情况。
代理模式结构 {#%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F%E7%BB%93%E6%9E%84}
静态代理和动态代理 {#%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86%E5%92%8C%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86}
在代码中,一般代理会被理解为代码增强,实际上就是在原代码逻辑前后增加一些代码逻辑,而使调用者无感知。
根据代理的创建时期,代理模式分为静态代理和动态代理。
- 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
- 动态:在程序运行时,运用反射机制动态创建而成
静态代理 ,其特点是代理类和目标类在代码中是确定的,因此称为静态。静态代理可以在不修改目标对象功能的前提下,对目标功能进行扩展。
但是静态代理显然不够灵活,这时就需要动态代理。 动态代理也叫 JDK 代理或接口代理,有以下特点:
代理对象不需要实现接口
代理对象的生成是利用 JDK 的 API 动态的在内存中构建代理对象
能在代码运行时动态地改变某个对象的代理,并且能为代理对象动态地增加方法、增加行为
一般情况下,动态代理的底层不用我们亲自去实现,可以使用线程提供的 API 。例如,在 Java 生态中,目前普遍使用的是 JDK 自带的代理和 GGLib 提供的类库。
JDK 实现代理只需要使用 newProxyInstance
方法,该方法需要接收三个参数,语法格式如下:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h )
注意该方法在 Proxy 类中是静态方法,且接收的三个参数说明依次为:
ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,把当前执行目标对象的方法作为参数传入
静态代理和动态代理主要有以下几点区别:
- 静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,则代理类需要同步增加,违背开闭原则。
- 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
- 若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码