代理模式(Proxy Pattern)是一种结构型设计模式,它为对象提供一个替身或占位符,以便控制对对象的访问。通过代理模式,客户端可以间接地与真实对象交互,而无需直接处理复杂的逻辑或操作。
核心角色
代理模式通常包含以下三个角色:
-
抽象主题(Subject)
定义代理和真实对象的共同接口,客户端通过这个接口与代理对象和真实对象交互。
-
真实主题(RealSubject)
具体实现抽象主题接口的类,表示被代理的对象,负责处理实际业务逻辑。
-
代理(Proxy)
实现抽象主题接口,内部持有真实主题的引用,负责控制对真实对象的访问,可能会在访问前后增加额外的功能(如权限校验、延迟加载、日志记录等)。
分类
-
静态代理
代理类在编译时就已经生成,代理类的代码由开发者手动编写或通过工具生成。
优点:简单直观。
缺点:每增加一个真实类都需要编写对应的代理类,维护工作量大。
-
动态代理
代理类在运行时动态生成,利用反射机制实现。Java 中常用的动态代理实现有两种:
JDK 动态代理(基于接口)
CGLIB 动态代理(基于继承)
优点:灵活、可以复用通用逻辑。
缺点:实现较复杂,性能可能略低。
静态代理示例
java
// 抽象主题
interface Subject {
void request();
}
// 真实主题
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("处理真实请求...");
}
}
// 代理类
class ProxySubject implements Subject {
private RealSubject realSubject;
@Override
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
System.out.println("代理开始处理...");
realSubject.request();
System.out.println("代理处理完成...");
}
}
// 测试
public class StaticProxyDemo {
public static void main(String[] args) {
Subject proxy = new ProxySubject();
proxy.request();
}
}
2. 动态代理示例(JDK)
java
复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 抽象主题
interface Subject {
void request();
}
// 真实主题
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("处理真实请求...");
}
}
// 动态代理处理器
class ProxyHandler implements InvocationHandler {
private Object target;
public ProxyHandler(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 DynamicProxyDemo {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Subject proxy = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
new ProxyHandler(realSubject)
);
proxy.request();
}
}
CGLIB 动态代理(基于继承)
CGLIB 动态代理是通过继承方式实现的动态代理,它是 JDK 动态代理的一种补充,尤其在被代理类没有实现接口时非常有用。CGLIB(Code Generation Library)基于字节码生成技术,通过扩展被代理类来实现代理功能。
CGLIB 动态代理的特点
- 基于继承:代理类是目标类的子类,因此目标类不能是 final 类型(因为 final 类无法被继承)。
- 无需接口:不同于 JDK 动态代理,CGLIB 不依赖接口,可以代理任何普通类。
- 性能较高:CGLIB 在生成代理类的运行性能上比 JDK 动态代理更快,但在生成代理类的初始性能上稍慢。
使用场景
- 目标类没有接口。
- 需要对方法调用添加通用逻辑,如日志、权限校验、事务管理等。
CGLIB 动态代理的实现步骤
- 引入 CGLIB 依赖(如果未集成 Spring,可以单独引入)。
- 定义目标类(无需实现接口)。
- 创建 MethodInterceptor,用来拦截方法调用。
- 使用 Enhancer 生成代理类。
xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 目标类
class RealSubject {
public void request() {
System.out.println("处理真实请求...");
}
}
// 自定义 MethodInterceptor
class ProxyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("CGLIB 代理:开始处理...");
Object result = proxy.invokeSuper(obj, args); // 调用父类方法
System.out.println("CGLIB 代理:处理完成...");
return result;
}
}
// 测试
public class CGLIBProxyDemo {
public static void main(String[] args) {
// 创建 Enhancer
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class); // 设置目标类
enhancer.setCallback(new ProxyInterceptor()); // 设置回调
// 生成代理类
RealSubject proxy = (RealSubject) enhancer.create();
// 调用代理方法
proxy.request();
}
}
注意事项
- 目标类不能是 final 类:CGLIB 通过继承实现代理,因此被代理的类不能用 final 修饰。
- 目标方法不能是 final 或 static:因为 final 方法不能被重写,而 static 方法属于类而非对象,也无法被代理。
- 性能影响:虽然 CGLIB 性能较高,但它的字节码生成过程稍有开销,不适合频繁创建代理的场景。
与 JDK 动态代理的对比
特性 | JDK动态代理 | CGLIB 动态代理 |
---|---|---|
实现方式 | 基于接口 | 基于继承 |
是否需要接口 | 必须有接口 | 无需接口 |
代理机制 | 通过反射调用目标对象的方法 | 通过生成子类并重写方法 |
性能 | 代理类生成速度较快,运行速度略慢 | 代理类生成速度较慢,运行速度较快 |
限制 | 无法代理没有实现接口的类 | 无法代理 final 类和 final 方法 |
CGLIB 动态代理非常适合在目标类未实现接口或需要高性能场景下使用,但需注意其基于继承的限制。
适用场景
- 控制对象访问:如权限校验或安全检查。
- 延迟加载:如在访问大对象时延迟其初始化。
- 记录日志:在方法调用前后添加日志。
- 远程代理:为不同地址空间的对象提供访问。
- 缓存代理:对结果进行缓存以提升性能。
- 通过代理模式,可以有效地对对象的访问进行控制和增强,是软件设计中常用的一种模式。