天天看点

设计模式--代理模式

定义: Provide a surrogate(代理) or placeholder(占位符) for another object to control access(控制访问) or append ability(赋能) to it. 类型: 结构型模式 场景: 访问控制,赋能 思路: 为调用者和被调用者之间留有余地,以应对变化。

在调用者和被调用者之间添加一个代理层,由代理层控制目标对象的引用,作为被调用者的替身,增加了访问的封装性、灵活性、扩展性。

封装性:代理封装了被调用者的访问细节,不需要上层调用者了解细节和介入实现;

灵活性:代理层对被调用者的时机、方式可以灵活控制;

扩展性:代理层可以给被调用者赋予新的能力,或者强化已有能力。

代理模式给调用者和被调用者之间增加了一个缓冲,从而为以后的变化留有余地,从一定程度上降低了耦合。

设计模式--代理模式
设计模式--代理模式

代理模式主要包含如下4个角色:

Subject(被调用者的抽象):定义被调用者想要/能够提供的服务。request()方法是要提供的服务;

注:此处对于抽象类的理解既可以是一个抽象类(abstract class),也可以是一个或者多个接口(interface),根据被访问对象实际需要暴露和提供的能力而定,不需要局限于类图。

RealSubject(被调用者的具体实现):服务的具体实现;

Client(调用者) : 服务的调用者,不局限于单个对象,可以是任何上层应用或者服务;

Proxy(访问代理):作为"代理"存在于调用者和被调用者之间,代替调用者访问目标服务;代理可以自行决定何时以及何种方式调用request()方法,同时提供额外的before()、after()等额外方法用于扩展目标服务的能力;

注:设计模式提供的是一种设计思路,所以代理模式的实现并不局限于类图的实现,其中的角色依据看待问题的视角可大可小,即可以是一个具体的类或者对象,也可以是一个组件、一个服务、一种资源...

代理模式中的Proxy和RealSubject就好比经纪人和明星的关系。经纪人负责明星的公共事务,比如:签约、谈片酬、财务管理,危机公关等,这些事情都可以划在Subject范畴,即经纪人的本职工作。同时经纪人本身又可以做一些自己的事情,比如:transfer the actor's property and visit his wife sometime.

代理模式还有一种简化模式,即代理类直接继承被调用者,而无须继承抽象接口。根据继承的特性,子类可以通过super关键字调用父类的公有方法,从而实现代理操作。类图如下:

设计模式--代理模式

通过继承实现代理的虽然方式简单,但是很显然违背了合成复用原则。然而我们不是为了模式而模式,原则只是为了更好的指导软件开发,而并非强制的规则,有时为了满足业务需求,违背原则也无妨。简化模式的具体实现如下,RealSubject无变化,此处仅列出代理类:

我们常用的cglib在实现动态代理时候,用的即是这种方式,将动态生成的代理类作为被调用类的子类,以获得访问权限。

要访问的资源在远端,通过代理封装远程资源的访问,屏蔽操作的复杂性。调用者调用代理对象如同调用本地对象一样,对调用者透明。远程代理可以作为分布式RPC的实现思路,比如:分布式服务框架Dubbo通过创建本地Service来代理远程服务,实现透明化的远程方法调用,即是一种远程代理的实现。

"以小见大"、"懒加载",对于创建或者加载消耗性能较大的资源,可以先创建一个消耗较小的代理,等到真正需要调用资源的时候,再通过代理进行资源的创建和加载,实现"按需加载"。代理可以是资源的"缩略图",也可以是资源的一部分。

实现资源访问权限控制,通过在代理层增加权限判断,保护资源的安全性。可以作为权限控制的实现思路。

代理对被调用这的结果进行缓存,从而降低反复调用带来的性能消耗。比如:通过spring-cache将查询结果缓存到内存中,对于相同条件的调用,直接访问缓存,提高查询性能,便是缓存代理的实现。

通过代理对象实现对被调用者的调用同步,防止冲突,保证并发安全;

通过代理实现对象的引用计数。

通过代理记录对象访问日志;

对于分布式系统,提供统一的代理实现。使得访问分布式系统和访问单点无差别。比如:twemproxy作为redis集群的代理,屏蔽了redis集群底层分片部署的复杂性,在客户端看来和直接访问redis单点无差别。

一定程度上降低了耦合;

降低调用服务的复杂性;

降低消耗,提高性能;(虚拟代理、缓存代理)

提升安全性;(保护代理)

相比于直接调用而言,增加了一层,增加请求耗时;

实现代理本身较为复杂;

1)AOP(Aspect Oriented Programming,面向切面的编程),通过预编译技术或者运行时动态生成字节码技术实现对原有类/对象能力的增强。通常分为"静态代理"和"动态代理"两类,"静态"和"动态"是根据生成AOP代理类的时机进行的划分,对应到"编译时"和"运行时"两个阶段。

静态代理,就是在.java文件编译成.class文件时,通过更改.class的字节码的方式为原有类生成AOP代理,在jvm加载类文件后,字节码已经固定,不会再有改变,因而又称为编译时增强。代表就是AspectJ框架。

动态代理,是通过动态字节码生成技术,在运行时"临时"生成AOP代理对象,代理对象对应的类文件是在运行时动态编译生成的,因而又称为运行时增强。常用工具有JDK原生的动态代理,以及Cglib动态搭理。

原则

符合

违背

描述

单一职责

Client、RealSubject仅负责自己的业务实现,不用关注职责以外的事情;

里氏替换原则

Proxy关联Suject而非RealSubject,能够代理具体子类的操作;

依赖倒转原则

同上,Proxy本身针对Subject抽象编程,没有针对具体的RealSubject实现编程;

接口隔离原则

Subject如果划分合理,可以做到接口隔离,比如屏蔽不需要代理的方法,由RealSubject在子类中实现;

最小知识原则

同上,Subject如果划分合理,仅暴露需要上层知道的服务,则符合;

开闭原则

修改具体的RealSubject,对上层不可见;如需要新增服务,可以通过实现新的RealSubject来扩展,故符合;

合成复用原则

Proxy调用RealSubject,使用IOC方式,属于关联关系,故符合;

装饰模式,状态模式;

1)代理模式和装饰模式、状态模式有何区别?

2)静态代理和动态代理有何异同?

3)JDK原生动态代理与cglib动态代理有何异同?

4)为什么被final修饰的方法不能够被重写?

<a href="http://blog.csdn.net/wenbingoon/article/details/8988553">Spring AOP 实现原理----AspectJ与CGLIB介绍</a>

<a href="http://blog.csdn.net/sunxianghuang/article/details/52094859">JVM即时编译(JIT)</a>