代理模式(Proxy Pattern)详解
代理模式(Proxy Pattern)是一种结构型设计模式,主要用于为某对象提供一个代理对象,以控制对该对象的访问。代理对象通常在目标对象前进行操作,从而增强目标对象的功能。这种模式为原对象提供了一种间接访问的方式,可以增加额外的功能(如权限控制、延迟加载、日志记录等)而不修改目标对象的代码。
1. 代理模式的基本概念
1.1 什么是代理模式?
代理模式的核心思想是使用代理对象来控制对目标对象的访问。代理对象与目标对象实现相同的接口,因此它们可以相互替换。代理对象通常包含对实际目标对象的引用,并可以在调用目标对象的方法之前或之后添加额外的操作。
1.2 代理模式的结构
代理模式通常包含以下几个角色:
- 抽象主题(Subject):定义了真实对象和代理对象的共同接口,客户端通过该接口与真实对象进行交互。
- 真实主题(RealSubject):实现了抽象主题接口,是真正处理请求的对象。
- 代理(Proxy):实现了抽象主题接口,并持有一个对真实主题的引用。代理对象可以在调用真实主题的方法前后执行额外的操作。
1.3 代理模式的类型
代理模式根据用途不同,可以分为以下几种常见类型:
- 静态代理(Static Proxy):代理类在编译时就已经确定,代理类与目标类实现相同的接口。
- 动态代理(Dynamic Proxy) :代理类在运行时动态生成,通常使用 Java 的
java.lang.reflect.Proxy
或 CGLIB 库来实现。 - 保护代理(Protection Proxy):控制对目标对象的访问权限。
- 虚拟代理(Virtual Proxy):通过代理对象来延迟对真实对象的创建或初始化。
- 缓存代理(Cache Proxy):通过代理对象在一定时间内缓存目标对象的结果,从而提升性能。
2. 代理模式的实现方式
2.1 静态代理
静态代理是一种在编译期就确定的代理模式。代理类需要实现与目标类相同的接口,客户端通过代理类调用目标对象的方法。静态代理通常用于在方法调用前后添加额外操作,比如日志记录、安全检查等。
示例代码:
java
// 抽象主题接口
public interface Service {
void performAction();
}
// 真实主题类
public class RealService implements Service {
@Override
public void performAction() {
System.out.println("执行真实操作");
}
}
// 代理类
public class ServiceProxy implements Service {
private RealService realService;
public ServiceProxy() {
this.realService = new RealService();
}
@Override
public void performAction() {
System.out.println("代理操作前的处理");
realService.performAction();
System.out.println("代理操作后的处理");
}
}
// 测试客户端
public class Client {
public static void main(String[] args) {
Service service = new ServiceProxy();
service.performAction();
}
}
输出:
java
代理操作前的处理
执行真实操作
代理操作后的处理
在这个示例中,ServiceProxy
类在调用 RealService
的方法前后分别执行了额外的操作。静态代理的缺点是需要为每个业务对象编写代理类,导致代码量增加,灵活性差。
2.2 动态代理
动态代理是在运行时动态生成代理类,而不是在编译时生成。Java 提供了 java.lang.reflect.Proxy
类用于创建动态代理,它基于反射机制实现,因此代理类不需要实现接口。动态代理的优势是减少了重复的代理类代码,并且能够在运行时灵活地处理方法调用。
JDK 动态代理
示例代码:
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 抽象主题接口
public interface Payment {
void pay(int amount);
}
// 真实主题类
public class RealPayment implements Payment {
@Override
public void pay(int amount) {
System.out.println("支付金额:" + amount + " 元");
}
}
// 动态代理处理器
public class PaymentInvocationHandler implements InvocationHandler {
private Object target;
public PaymentInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("支付前的安全检查");
Object result = method.invoke(target, args);
System.out.println("支付后的日志记录");
return result;
}
}
// 测试客户端
public class Client {
public static void main(String[] args) {
Payment realPayment = new RealPayment();
Payment proxyPayment = (Payment) Proxy.newProxyInstance(
realPayment.getClass().getClassLoader(),
realPayment.getClass().getInterfaces(),
new PaymentInvocationHandler(realPayment)
);
proxyPayment.pay(100);
}
}
输出:
java
支付前的安全检查
支付金额:100 元
支付后的日志记录
在这个示例中,动态代理类在运行时通过 Proxy.newProxyInstance()
方法动态创建。动态代理的优势在于它能够减少重复的代码,并在运行时灵活地处理不同的方法调用。
CGLIB 动态代理
JDK 动态代理只能代理实现了接口的类,而 CGLIB(Code Generation Library) 是一个开源的字节码生成库,它能够为没有实现接口的类创建动态代理。
示例代码:
java
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 目标类
public class BookService {
public void addBook() {
System.out.println("添加书籍");
}
}
// CGLIB 动态代理类
public class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
public Object getProxyInstance() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("操作前的检查");
Object result = proxy.invokeSuper(obj, args);
System.out.println("操作后的日志");
return result;
}
}
// 测试客户端
public class Client {
public static void main(String[] args) {
BookService bookService = new BookService();
CglibProxy proxy = new CglibProxy(bookService);
BookService proxyService = (BookService) proxy.getProxyInstance();
proxyService.addBook();
}
}
输出:
java
操作前的检查
添加书籍
操作后的日志
CGLIB 通过继承目标类生成代理类,因此它适用于那些没有实现接口的类,但不能代理 final
类和 final
方法。
3. 代理模式的应用场景
- 权限控制:通过代理对象控制对目标对象的访问权限。
- 日志记录:在方法调用前后添加日志记录操作。
- 事务管理:在方法调用前开启事务,调用后提交或回滚事务。
- 缓存机制:通过代理对象在一定时间内缓存目标对象的结果,从而提升性能。
- 延迟加载:代理对象延迟对真实对象的创建或加载,以节省资源。
4. 代理模式的优缺点
4.1 优点
- 解耦:客户端无需关心真实对象的实现,只需依赖代理对象即可。
- 增强功能:代理对象能够在调用真实对象的方法前后添加额外功能,而无需修改原有代码。
- 灵活性:通过动态代理,能够在运行时动态添加功能,提高系统的灵活性。
4.2 缺点
- 性能开销:由于代理模式增加了方法调用的中间层,可能会带来性能开销,尤其是动态代理和反射的使用。
- 代码复杂度:引入代理模式会增加系统的复杂度,特别是在大量使用代理模式时,可能导致代码难以理解和维护。
5. 总结
代理模式是一种非常有用的设计模式,能够在不修改目标对象的情况下增强其功能。通过静态代理和动态代理的不同实现方式,可以根据具体场景选择最合适的代理模式。代理模式在实际开发中应用广泛,尤其是在权限控制、日志记录、缓存管理和事务处理等方面发挥了重要作用。理解和灵活应用代理模式,将有助于开发出更为灵活、可维护的系统。