代理模式的定义与特点
定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
代理模式的主要优点有:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能,比如请进行拦截、修改或增强;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度。
其主要缺点是:
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度。
代理模式的结构与实现
代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问,下面来分析其基本结构和实现方法。类图关系如下:
代码如下:
Subject.java
public interface Subject {
void request();
}RealSubject.java
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("do something");
}
}Proxy.java
public class Proxy implements Subject {
private Subject subject;
public Proxy() {
this.subject = new RealSubject();
}
private void preRequest() {
System.out.println("动态代理前处理");
}
@Override
public void request() {
preRequest();
if (subject != null) {
subject.request();
}
postRequest();
}
private void postRequest() {
System.out.println("动态代理后处理");
}
}ProxyTest.java
public class ProxyTest {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.request();
}
}实际应用
1. 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
2. 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
3. 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
4. 延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载。
5. 缓存代理(Cache Proxy): 代理对象可以缓存原始对象的结果,以便在后续相同请求时能够直接返回缓存的结果,减少重复计算。
6. 日志记录代理(Logging Proxy): 代理对象可以在访问原始对象之前或之后记录日志,用于调试、监控或审计。
代理模式的扩展
上面的代理示例中,代理类中包含了对真实主题的引用。这种方式有2中缺点:1、真实主题与代理主题要一一对应;2、增加真实主题也要增加代理主题。
上面的代理示例,属于静态代理模式。相对应的有动态代理模式,Java中实现了多种动态代理,如:JDK动态代理(InvocationHandler)和CGLIB动态代理(不需要接口)。下面是基于JDK动态代理的示例代码:
InvocationHandlerTest.java
public class InvocationHandlerTest {
public static void main(String[] args) {
InvocationHandler handler = new InvocationHandler() {
private Subject subject = new RealSubject();
private void preRequest() {
System.out.println("动态代理前处理");
}
private void postRequest() {
System.out.println("动态代理后处理");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
preRequest();
method.invoke(subject, args);
postRequest();
return null;
}
};
Subject subject = (Subject)java.lang.reflect.Proxy.newProxyInstance(
Subject.class.getClassLoader(), new Class[] {Subject.class}, handler);
subject.request();
}
}关于CGLIB动态代理的使用,以及与JDK动态代理之间的区别等,大家可以自行研究一下。
