设计模式 - 代理模式
一、引入
当你买火车票时,你通常会通过网络或者售票窗口进行购买,而不是直接去火车站找列车。在这个过程中,售票窗口或者网站就像是一个代理,它帮你与列车取得联系,最终完成了购票的任务。
同样地,代理模式就是为了解决类似的问题。它引入了一个代理对象,帮助你访问某个对象,同时可以在访问前后执行一些额外的操作。这个代理对象担当了一个中介的角色,让你可以通过它来操作真正的对象,而无需直接与真正的对象打交道。
举个简单例子,假设你想下载一个大文件,但你担心下载过程中网络断开,所以你找了一个下载管理器来帮你下载。这个下载管理器就是一个代理,它会在下载前检查网络状态、控制下载速度等操作,然后再帮你实际下载文件。
代理模式就是这样,它为你提供了一个中间层,使得你可以通过代理来操作某个对象,同时可以在操作前后执行一些额外的操作,这样就可以保证原始对象的安全性和灵活性。
二、概念
代理模式(Proxy Pattern)是一种结构型设计模式,它允许一个对象(代理对象)作为另一个对象(真实对象)的接口,以控制对该对象的访问。
代理模式可以在不改变真实对象的情况下,提供对其的间接访问。这种访问的方式可以用于很多用途,比如控制对对象的访问权限、在访问前后执行一些额外的操作等。
静态代理、动态代理、CGLIB代理
三、基本结构
- 接口(Subject):定义了真实对象和代理对象之间的共同接口,以便代理对象可以用来代理真实对象。
- 真实对象(Real Subject):是被代理的对象,也是代理对象所代表的对象。代理对象将请求传递给真实对象来执行实际的操作。
- 代理对象(Proxy):实现了接口,并包含了对真实对象的引用。代理对象通常在其内部包含了一个实例变量,该变量引用了真实对象,以便可以在适当的时候将请求传递给真实对象。
四、示例代码
1、静态代理
/**
* 接口
*/
public interface Image {
void display();
}
/**
* 真实对象
*/
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk(fileName);
}
private void loadFromDisk(String fileName) {
System.out.println("Loading " + fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
}
/**
* 代理对象
*/
public class ProxyImage implements Image {
private String fileName;
private RealImage realImage;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void display() {
if (fileName.contains("zf")) {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}
}
}
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
Image image = new ProxyImage("hazfha");
// 图像将从磁盘加载
image.display();
// 图像不会从磁盘加载
image.display();
}
}
2、动态代理
//接口
public interface Subject {
void doSomething();
}
/**
* 真实对象
*/
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("do something");
}
}
/**
* 动态代理 : 实现InvocationHandler接口,接口中包含了一个 invoke 方法,用于在代理对象调用方法时进行逻辑处理。通常我们会自定义 * 一个类实现这个接口,并在 invoke 方法中编写处理逻辑。
*/
public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理前");
Object invoke = method.invoke(target, args);
System.out.println("代理后");
return invoke;
}
}
/**
* 动态代理
*/
public class Client {
public static void main(String[] args) {
// 创建真实主题
Subject realSubject = new RealSubject();
// 使用真实主题创建代理主题
//loader:类加载器,用于加载代理类。
//interfaces:代理类要实现的接口。
//h:实现 InvocationHandler 接口的对象,用于处理方法调用。
// Proxy 类:这是 Java 提供的用于创建动态代理对象的类。
//它的 newProxyInstance 方法可以接受一个类加载器、一组接口和一个 InvocationHandler 对象,然后返回一个代理对象。
Subject proxySubject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
new DynamicProxy(realSubject));
// 调用代理主题的方法
proxySubject.doSomething();
}
}
3、CGLIB代理
/**
* 接口
*/
public interface Subject {
void doSomething();
}
/**
* 真实对象
*/
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("do something");
}
}
/**
* CGLIB动态代理 : 实现MethodInterceptor接口
*/
public class DynamicProxy implements MethodInterceptor {
private Object target;
public DynamicProxy(Object object) {
this.target = object;
}
//obj:表示代理对象。
//method:表示被调用的方法。
//args:表示方法的参数数组。
//proxy:表示方法的代理。
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理前");
Object invoke = method.invoke(target, objects);
System.out.println("代理后");
return invoke;
}
}
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
//创建一个 Enhancer 对象,用于生成代理类。
Enhancer enhancer = new Enhancer();
// 设置代理类的父类,也就是目标类
enhancer.setSuperclass(RealSubject.class);
// 设置回调函数,即在代理类调用方法时,会调用回调函数中的方法
enhancer.setCallback(new DynamicProxy(realSubject));
// 通过 enhancer.create() 方法生成代理对象。
RealSubject realSubject1 = (RealSubject) enhancer.create();
// 调用代理对象的方法
realSubject1.doSomething();
}
}
五、用途
- 控制访问权限:代理可以作为一个门卫,控制对真实对象的访问权限。只有符合一定条件的请求才会被代理对象转发给真实对象。
- 延迟加载:代理可以在需要时才实例化真实对象,从而实现延迟加载。这在需要消耗大量资源的情况下特别有用。
- 实现缓存:代理可以缓存对真实对象的请求的结果,当相同的请求再次发生时,代理可以直接返回缓存的结果,而不必再次调用真实对象。
- 实现日志记录:代理可以在调用真实对象的方法之前或之后记录日志,从而实现日志记录功能。
- 实现安全检查:代理可以在调用真实对象的方法之前进行安全检查,确保调用者有权执行该操作。
- 实现事务管理:代理可以在调用真实对象的方法之前开启事务,在方法执行后提交或回滚事务。
- 实现远程代理:代理可以在不同的地址空间中代表真实对象,实现远程调用。
- 实现虚拟代理:代理可以在真实对象加载过程中表现得像真实对象一样,直到需要真正的结果。
- 实现保护代理:代理可以检查调用者的权限,以确保它们具有执行操作的权限。
- 实现动态代理:可以在运行时动态创建代理类,而不需要静态定义代理类。
六、总结
优点:
- 控制访问:代理模式可以在真实主题类的方法执行前后,进行一些控制,比如权限控制、验证等。
- 保护隐私:代理模式可以隐藏真实主题的实现细节,保护了真实主题的隐私。
- 延迟加载:虚拟代理可以延迟加载真实主题,节省资源开销。比如在创建代理对象时,不会立即创建真实对象,而是在需要时才创建。
- 简化调用:代理模式可以为真实主题提供一个简化的接口,客户端可以直接调用代理,而无需关心真实主题的复杂性。
缺点:
- 增加复杂性:引入了代理类,可能会增加系统的复杂性。
- 性能损耗:在访问真实主题之前或之后,代理可能会执行一些额外的操作,导致性能损耗。