天天看点

代理模式 代理模式

标签 : Java与设计模式

为其他对象提供一种<code>代理</code>以控制对这个对象的访问(可以详细控制访问某个对象的方法, 在调用这个方法[前/后]做[前/后]置处理, 从而实现将统一流程放到代理类中处理).

我们书写执行一个功能的函数时, 经常需要在其中写入与功能不是直接相关但很有必要的代码(如日志记录,事务支持等);这些枝节性代码虽然是必要的,但它会带来以下麻烦:

枝节性代码游离在功能性代码之外,它不是函数的目的,这是对<code>OO</code>是一种破坏;

枝节性代码会造成功能性代码对其它类的依赖,加深类之间的耦合,会造成功能性代码移植困难,可重用性降低, 这是OO系统所竭力避免的;

从正常角度来说: 枝节性代码应该<code>监视</code>着功能性代码,然后采取行动,而不是功能性代码<code>通知</code>枝节性代码采取行动,这好比吟游诗人应该是主动记录骑士的功绩而不是骑士主动要求诗人记录自己的功绩

Java代理分类

静态代理: 手动定义代理类

动态代理: 动态生成代理类

JDK自带的动态代理

JavaAssist字节码操作库实现

CGLib

ASM(底层使用指令, 可维护性差)

代理中的角色

抽象接口:声明真实对象和代理对象的共同接口

代理对象:代理对象内部包含<code>真实对象的引用</code>,从而可以操作真实对象; 同时,代理对象与真实对象有相同的接口,能在任何时候代替真实对象,而且代理可以在真实对 象前后加入特定的逻辑以实现功能的<code>扩展</code>;

真实对象:代理对象所代表的对象;是我们最终要引用的对象

我们模拟请明星唱歌这个过程,但大家都知道要请明星唱歌(比如周杰伦)是一件比较麻烦的事情, 比如唱歌前要签约, 唱歌之后还有收款, 而平时明星们都是比较忙的, 想签约, 收款这些事情一般都是由他的助手来代理完成的,而明星只负责唱歌就行了, 像签约与收款这种事情就可以算作是明星的增强, 虽然这不是明星的主要目的, 但是这个流程是必须要有的.

目标接口

真实对象

代理对象

自己并未实现业务逻辑接口,而是调用真实角色来实现:

Client

可以看出,客户实际想要调用的是<code>RealStar</code>的<code>singSong</code>方法,现在用<code>StaticProxy</code>来代理<code>RealStar</code>类,也可以达到同样的目的,同时还封装了其他方法(像<code>singContract``collectMoney</code>),可以处理一些其他流程上的问题.

如果要按照上述的方法使用代理模式,那么<code>真实角色</code>必须是<code>事先已经存在的</code>,并将其作为代理对象的内部属性;但是实际的Java应用中, 如果有一批真实对象, 而毎个代理对象只对应一个真实对象的话,会导致类的急剧膨胀;此外,如果我们事先并不知道真实角色,那么该如何使用编写代理类呢?这个问题可以通过java的<code>动态代理机制</code>来解决.

所谓动态代理是这样一种<code>class</code>:它是在运行时生成的class,在生成它时你必须提供一组<code>interface</code>给它,然后该class就宣称它实现了这些 interface.

JDK对动态代理提供了以下支持:

<code>java.lang.reflect.Proxy</code> 动态生成代理类和对象

<code>java.lang.reflect.InvocationHandler</code>

可以通过invoke方法实现对真实角色的代理访问;

每次通过Proxy生成代理类对象时都要指定对象的处理器对象.

首先, <code>Star</code>接口可以精简一下, 只做他该做的事情:

Star

RealStar

当执行动态代理对象里的方法时, 实际上会替换成调用InvocationHandler中的invoke方法.

<code>InvocationHandler</code>: 用于实现代理

动态代理虽然可以使得我们不用在手写代理对象的代码,但是<code>InvocationHandler</code>还是面向特定的抽象接口(如Star)的来写的; 而代理工厂可以让我们的代码写的更加<code>抽象</code>(而不必面向确定的抽象接口写代码).

代理工厂的目标是<code>目标对象和增强方法皆可改变</code>, 这个模式在现实中的表现就是:

a. 明星对代理并不一定是从一而终的, 明星随时都可能会换代理(助手);

b. 明星不一定只会唱歌, 他还有可能会跳舞.

c. 代理可能不只是为一个明星服务

这样, 我们就实现一个代理工厂-可以随意更换代理所做的辅助性工作; 而目标对象也可以随时增加新的方法.

可以看到, <code>ProxyFactory</code>与<code>Start</code>是没有任何关系的, 他们之间能够联系其他完全是靠Client来促成.

代理工厂

<code>Star</code>和<code>RealStar</code>同前

现在, 我们的对明星要求比较高了, 他不光要会唱歌, 还要会跳舞.

此时, 我们的<code>client</code>什么都不需要改, 只是添加一个调用就可:

而且在实际开发中, 这些增强类还可以从配置文件中读取(像Spring).

这种代理在<code>AOP(Aspect Orient Programming: 面向切面编程)</code>中被成为<code>AOP代理</code>,AOP代理包含了目标对象的全部方法, 但AOP代理中的方法与目标对象的方法存在差异: 比如可以在执行目标方法之前/后插入一些通用的处理(增强).

当Client需要调用某个对象时,客户端实际上也不关心是否准确得到该对象,Client要只是一个能提供该功能的对象而已,因此我们就可返回该对象的代理(Proxy).<code>代理</code>就是在访问对象时引入一定程度的<code>间接性</code>, 由于存在这种间接性, 我们就可以做很多工作:

远程代理: 为一个对象在不同的地址空间提供<code>局部代表</code>, 这样可以隐藏一个对象存在于不同地址空间的事实(Dubbo实现);

安全代理: 屏蔽对真实角色的访问, 用代理来控制对真实对象的访问权限;

延迟加载: 先加载轻量级代理对象,真正需要时再加载真实对象.

<dl></dl>

<dt>参考:</dt>