所访问的真实对象与代理对象需要实现相同的接口
根据代理模式适用目的的不同,分为保护代理、远程代理、虚拟代理、缓冲代理等
代理模式是一种对象结构型模式
代理对象在客户端对象和目标对象之间起到中介的作用 它去掉客户端不能看到的内容和服务或者增加客户需要的额外的新服务
代理模式包含以下三个角色:
(1)Subject 抽象主题角色:它声明了真实主题和代理主题的共同接口,这样一来在任何适用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色
进行编程
(2)Proxy 代理主题角色 它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象,在代理主题角色中提供了一个与真实主题角色相同的接口,以便在任何时候
都可以替代真实主题;代理主题角色还可以控制对真实主题的引用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。通常 在代理主题角色
中客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象的操作。
(3)RealSubject(真实主题角色):它定义了代理角色所表示的真实对象,在真实主题中实现了真实的业务操作。客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。
代理模式的实现
抽象主题类角色声明了真实主题类和代理类的公共方法,它可以是接口、抽象类或具体类。客户端针对抽象主题类编程,一致地对待真实主题和代理主题。典型地抽象主题代码如下:
public abstract class Subject {
public abstract void request();
}
真实主题类继承了抽象主题类,提供了业务方法地具体实现。
public class RealSubject extends Subject {
public void request() {
//业务方法的具体实现代码
}
}
代理类也是抽象主题类的子类,它维持一个对真实主题对象的引用,调用在真实主题中实现的业务方法
在调用时可以在原有业务方法的基础上附加一些新的方法对功能进行扩展和约束。
public class Proxy extends Subject {
private RealSubject realSubject = new RealSubject();//维持一个对真实主题对象的引用
public void preRequest() {
...
}
public void request() {
preRequest();
realSubject.request();//调用真实主题对象的方法
postRequest();
}
public void postRequest() {
...
}
}
远程代理(Remote Proxy) : 为一个位于不同地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一台主机中,也可以在另一台主机中,
远程代理又称为大使(Ambassador) 远程代理对象承担了大部分网络通信工作,并负责对远程业务方法的调用。
RMI remote method invocation 实现远程代理
它能够实现一个Java虚拟机中的对象调用另一个Java虚拟机中的对象的方法。
在RMI中,客户端对象可以通过一个桩(Stub)对象与远程主机上的业务对象进行通信,由于桩对象和远程业务对象的接口一致,因此对于客户端而言
操作远程对象和本地桩对象没有任何区别,桩对象就是远程业务对象在本地主机的代理对象。
在RMI的实现过程中,远程主机端有一个Skeleton(骨架)对象来负责与Stub对象通信,RMI的基本使用步骤如下:
(1)客户端发起请求,将请求转交至RMI客户端的Stub类
(2)Stub类将请求的接口、方法、参数等信息进行序列化。
(3)将序列化后的流使用Socket传输至服务器端。
(4)服务器端接收到流后将其转发至相应的Skeleton类。
(5)Skeleton类将请求信息发序列化后调用实际的业务处理类
(6)业务处理类处理完毕之后将结果返回给Skeleton类。
(7)Skeleton类将结果序列化,再次通过Socket将流传送给客户端的Stub
(8)Stub在接收到流后进行反序列化,将反序列化后得到的Java Object对象返回给客户端调用者。
至此,一次完整的远程调用得以完成。
虚拟代理:Virtual Proxy:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
保护代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
缓冲代理 Cache Proxy 为某一个目标操作的结果提供临时的存储空间 以便多个客户端可以共享这些结果
智能引用代理(Smart Reference Proxy):当一个对象被引用时提供一些额外的操作,例如将对象被调用的次数记录下来等。
每一个代理类在编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法都被固定,这种代理方式被称为静态代理(Static Proxy).
如果需要为不同的真实主题类提供代理类或者代理一个真实主题类中的不同方法,都需要增加新的代理类,这将导致系统中类的个数急剧增加。
动态代理(Dynamic Proxy)可以让系统在运行时根据实际需要来创建动态代理类,让同一个代理类能够代理多个不同的真实主题类而且可以代理不同的方法。
动态代理是一种较为高级的代理模式,它在事务管理、AOP(Aspect-Oriented Programming 面向方面编程)等领域都发挥了重要作用。
java.lang.reflect
1.Proxy类
Proxy类提供了用于创建动态代理类和实例对象的方法,它是所创建的动态代理类的父类,它最常用的方法如下:
(1) public static Class<?> getProxyClass(ClassLoader loader,Class<?> ... interfaces):该方法用于返回一个Class类型的代理类
在参数中需要提供类加载器并需要指定代理的接口数组(与真实主题类的接口列表一致)
(2)public static Object newProxyInstance(ClassLoader loader,Class<?>[] interface,InvocationHandler h):该方法用于返回一个动态
创建的代理类的实例,方法中的第一个参数loader表示代理类的类加载器,第二个参数interfaces表示代理类所实现的接口列表(与真实主题 的
接口列表一致),第三个参数h表示所指派的调用处理程序类,
2.InvocationHandler接口
InvocationHandler接口是代理处理程序类的实现接口,该接口作为代理实例的调用处理者的公共父类,每一个代理类的实例都可以提供一个相关的
具体调用处理者(InvocationHandler接口的子类)。在该接口中声明了以下方法:
public Object invoke(Object proxy,Method method,Object[] args);
该方法用于处理对代理类实例的方法调用并返回相应的结果。当一个代理实例中的业务方法被调用时将自动调用该方法。invoke()方法包含3个参数,
其中第一个参数proxy表示代理类的实例,第二个参数method表示需要代理的方法。第三个参数args表示代理方法的参数数组。
动态代理类需要在运行时指定所代理的真实主题类的接口,客户端在调用动态代理对象的方法时调用请求自动转发给InvocationHandler对象的
invoke()方法 由invoke()方法实现请求的统一处理。