代理模式(Proxy Pattern)
一、模式概述
代理模式是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理对象和目标对象实现相同的接口,客户端通过代理对象间接访问目标对象。
核心思想:在不改变目标对象的前提下,提供额外的功能(如权限控制、日志记录、缓存等),通过代理来增强或控制对目标对象的访问。
二、代理的类型
| 代理类型 | 说明 | 典型应用 |
|---|---|---|
| 静态代理 | 编译时确定代理类 | 简单的增强逻辑 |
| JDK 动态代理 | 基于接口,运行时生成代理类 | Spring AOP 默认方式 |
| CGLIB 动态代理 | 基于继承,运行时生成子类 | Spring AOP(无接口时) |
| 远程代理 | 为远程对象提供本地代理 | RPC 框架 |
| 虚拟代理 | 延迟加载大对象 | 图片懒加载 |
| 保护代理 | 控制对象的访问权限 | 权限校验 |
三、示例1:静态代理
问题场景
对用户服务添加日志记录和性能监控功能。
java
// ========== 公共接口 ==========
/**
* 用户服务接口
*/
interface UserService {
void addUser(String name);
String findUser(int id);
void deleteUser(int id);
}
// ========== 目标类(真实对象) ==========
/**
* 用户服务实现
*/
class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println(" [服务] 添加用户: " + name);
// 模拟业务逻辑
try { Thread.sleep(100); } catch (InterruptedException e) { }
}
@Override
public String findUser(int id) {
System.out.println(" [服务] 查找用户, ID: " + id);
try { Thread.sleep(50); } catch (InterruptedException e) { }
return "User-" + id;
}
@Override
public void deleteUser(int id) {
System.out.println(" [服务] 删除用户, ID: " + id);
}
}
// ========== 静态代理类 ==========
/**
* 用户服务静态代理
* 在不修改目标类的情况下,添加日志和性能监控功能
*/
class UserServiceProxy implements UserService {
private final UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void addUser(String name) {
System.out.println("[代理-前置] 调用 addUser, 参数: " + name);
long start = System.currentTimeMillis();
target.addUser(name); // 调用目标对象的方法
long cost = System.currentTimeMillis() - start;
System.out.println("[代理-后置] addUser 执行完成, 耗时: " + cost + "ms");
}
@Override
public String findUser(int id) {
System.out.println("[代理-前置] 调用 findUser, 参数: " + id);
long start = System.currentTimeMillis();
String result = target.findUser(id); // 调用目标对象的方法
long cost = System.currentTimeMillis() - start;
System.out.println("[代理-后置] findUser 执行完成, 耗时: " + cost + "ms");
return result;
}
@Override
public void deleteUser(int id) {
// 权限检查(代理特有的功能)
System.out.println("[代理-权限] 检查删除权限...");
boolean hasPermission = true; // 模拟权限检查
if (!hasPermission) {
System.out.println("[代理-权限] 权限不足,拒绝删除!");
return;
}
System.out.println("[代理-前置] 调用 deleteUser, 参数: " + id);
target.deleteUser(id);
System.out.println("[代理-后置] deleteUser 执行完成");
}
}
// ========== 客户端代码 ==========
public class StaticProxyDemo {
public static void main(String[] args) {
// 创建代理对象
UserService proxy = new UserServiceProxy(new UserServiceImpl());
System.out.println("===== 添加用户 =====");
proxy.addUser("张三");
System.out.println("\n===== 查找用户 =====");
String user = proxy.findUser(1001);
System.out.println(" 结果: " + user);
System.out.println("\n===== 删除用户 =====");
proxy.deleteUser(1001);
}
}四、示例2:JDK 动态代理
原理
JDK 动态代理基于 java.lang.reflect.Proxy 和 InvocationHandler 接口,在运行时动态生成代理类。
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// ========== 公共接口 ==========
/**
* 支付服务接口
*/
interface PaymentService {
boolean pay(double amount, String orderId);
boolean refund(double amount, String orderId);
}
// ========== 目标类 ==========
/**
* 支付服务实现
*/
class AlipayService implements PaymentService {
@Override
public boolean pay(double amount, String orderId) {
System.out.println(" [支付宝] 支付 " + amount + " 元,订单号: " + orderId);
return true;
}
@Override
public boolean refund(double amount, String orderId) {
System.out.println(" [支付宝] 退款 " + amount + " 元,订单号: " + orderId);
return true;
}
}
// ========== 动态代理处理器 ==========
/**
* 通用的日志代理处理器
* 通过反射机制拦截方法调用,动态添加增强逻辑
*/
class LogInvocationHandler implements InvocationHandler {
private final Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
System.out.println("[动态代理-前置] 调用方法: " + methodName
+ ", 参数: " + java.util.Arrays.toString(args));
long start = System.nanoTime();
// 通过反射调用目标对象的真实方法
Object result = method.invoke(target, args);
long cost = (System.nanoTime() - start) / 1000;
System.out.println("[动态代理-后置] 方法: " + methodName
+ ", 返回值: " + result + ", 耗时: " + cost + "μs");
return result;
}
}
// ========== 安全代理处理器 ==========
/**
* 安全代理处理器
* 只允许特定方法被调用
*/
class SecurityInvocationHandler implements InvocationHandler {
private final Object target;
private final java.util.Set<String> allowedMethods;
public SecurityInvocationHandler(Object target, String... allowedMethods) {
this.target = target;
this.allowedMethods = new java.util.HashSet<>(java.util.Arrays.asList(allowedMethods));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!allowedMethods.contains(method.getName())) {
System.out.println("[安全代理] 方法 " + method.getName() + " 未授权,拒绝访问!");
return null;
}
System.out.println("[安全代理] 方法 " + method.getName() + " 已授权,允许执行");
return method.invoke(target, args);
}
}
// ========== 客户端代码 ==========
public class DynamicProxyDemo {
/**
* 创建动态代理对象的工具方法
*/
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target, InvocationHandler handler) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
}
public static void main(String[] args) {
AlipayService realService = new AlipayService();
// 1. 创建日志代理
System.out.println("===== 日志代理 =====");
PaymentService logProxy = createProxy(realService, new LogInvocationHandler(realService));
logProxy.pay(99.9, "ORDER001");
System.out.println();
logProxy.refund(30.0, "ORDER001");
System.out.println();
// 2. 创建安全代理(只允许 pay 方法)
System.out.println("===== 安全代理 =====");
PaymentService securityProxy = createProxy(realService,
new SecurityInvocationHandler(realService, "pay"));
securityProxy.pay(50.0, "ORDER002"); // 允许
System.out.println();
securityProxy.refund(50.0, "ORDER002"); // 拒绝
System.out.println();
// 3. 链式代理(日志 + 安全)
System.out.println("===== 链式代理(日志 + 安全) =====");
PaymentService chainedProxy = createProxy(realService,
new LogInvocationHandler(
createProxy(realService,
new SecurityInvocationHandler(realService, "pay", "refund"))
));
chainedProxy.pay(200.0, "ORDER003");
}
}五、示例3:CGLIB 动态代理
原理
CGLIB(Code Generation Library)通过生成目标类的子类来实现代理,不需要目标类实现接口。
java
// 注意:CGLIB 需要引入 cglib 依赖
// <dependency>
// <groupId>cglib</groupId>
// <artifactId>cglib</artifactId>
// <version>3.3.0</version>
// </dependency>
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 订单服务(没有实现接口)
*/
class OrderService {
public String createOrder(String product, int quantity) {
System.out.println(" [订单服务] 创建订单: " + product + " x " + quantity);
return "ORD" + System.currentTimeMillis();
}
public void cancelOrder(String orderId) {
System.out.println(" [订单服务] 取消订单: " + orderId);
}
}
/**
* CGLIB 动态代理处理器
*/
class OrderMethodInterceptor implements MethodInterceptor {
private final Object target;
public OrderMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, java.lang.reflect.Method method,
Object[] args, MethodProxy methodProxy) throws Throwable {
String methodName = method.getName();
System.out.println("[CGLIB代理-前置] 调用: " + methodName
+ ", 参数: " + java.util.Arrays.toString(args));
// 使用 methodProxy.invokeSuper 调用父类(目标类)的方法,性能更好
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("[CGLIB代理-后置] 调用: " + methodName
+ ", 返回: " + result);
return result;
}
}
/**
* CGLIB 代理工厂
*/
class CglibProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new OrderMethodInterceptor(target));
return (T) enhancer.create();
}
}
// ========== 客户端代码 ==========
public class CglibProxyDemo {
public static void main(String[] args) {
// CGLIB 不需要接口,直接代理类
OrderService proxy = CglibProxyFactory.createProxy(new OrderService());
System.out.println("===== 创建订单 =====");
String orderId = proxy.createOrder("iPhone 16", 1);
System.out.println(" 订单号: " + orderId);
System.out.println("\n===== 取消订单 =====");
proxy.cancelOrder(orderId);
}
}六、三种代理方式对比
| 特性 | 静态代理 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|---|
| 实现方式 | 手动编写代理类 | 基于反射,运行时生成 | 基于继承,运行时生成子类 |
| 接口要求 | 必须实现相同接口 | 必须基于接口 | 不需要接口,代理类的子类 |
| 性能 | 编译时确定,性能最好 | 反射调用,性能一般 | 字节码生成,性能较好 |
| 灵活性 | 低(每个接口都要写代理类) | 高(一个处理器代理所有接口) | 高(代理所有类) |
| Spring AOP | 不使用 | 默认方式(有接口时) | 无接口时使用 |
| 适用场景 | 简单场景,代理逻辑固定 | 通用的 AOP 增强 | 无接口的类的代理 |
代码结构对比
静态代理:
Interface ──implements──▶ RealClass
Interface ──implements──▶ ProxyClass (持有RealClass引用)
JDK 动态代理:
Interface ──implements──▶ RealClass
Proxy.newProxyInstance() ──生成──▶ $Proxy0 (运行时生成,实现接口)
CGLIB 动态代理:
RealClass
Enhancer.create() ──生成──▶ RealClass$$EnhancerByCGLIB (运行时生成,继承目标类)七、UML 类图
┌────────────┐ ┌────────────────┐
│ Client │───────▶│ Subject (接口) │
└────────────┘ └────────┬───────┘
│ 实现
┌─────────┴────────┐
│ │
┌──────┴──────┐ ┌──────┴──────┐
│RealSubject │ │ Proxy │
│(真实对象) │ │(代理对象) │
│+request() │ │-realSubject │
└─────────────┘ │+request() │
│ +预处理() │
│ +real.request()│
│ +后处理() │
└─────────────┘八、Spring AOP 中的代理模式
java
/**
* Spring AOP 底层就是代理模式的典型应用
*
* @Component
* @Aspect
*/
public class UserServiceAspect {
/**
* 前置通知:在方法执行前执行
*/
// @Before("execution(* com.example.service.*.*(..))")
public void before(JoinPoint joinPoint) {
System.out.println("[AOP前置] " + joinPoint.getSignature().getName());
}
/**
* 后置通知:在方法执行后执行
*/
// @After("execution(* com.example.service.*.*(..))")
public void after(JoinPoint joinPoint) {
System.out.println("[AOP后置] " + joinPoint.getSignature().getName());
}
/**
* 环绕通知:完全控制方法的执行
*/
// @Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("[AOP环绕-前] " + pjp.getSignature().getName());
Object result = pjp.proceed(); // 调用目标方法
System.out.println("[AOP环绕-后] " + pjp.getSignature().getName());
return result;
}
}
/**
* Spring 选择代理方式的逻辑:
*
* if (目标类实现了接口) {
* 使用 JDK 动态代理 // 默认
* } else {
* 使用 CGLIB 动态代理
* }
*
* // Spring Boot 2.x 默认使用 CGLIB
* // 可通过配置切换:
* spring.aop.proxy-target-class=false // 强制使用 JDK 动态代理
*/九、实际应用场景
1. 延迟加载(虚拟代理)
java
/**
* 图片虚拟代理
* 图片加载开销很大,只有在真正显示时才加载
*/
interface Image {
void display();
}
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk(); // 耗时操作
}
private void loadFromDisk() {
System.out.println(" [加载] 从磁盘加载图片: " + filename + " (耗时操作)");
try { Thread.sleep(1000); } catch (InterruptedException e) { }
}
@Override
public void display() {
System.out.println(" [显示] 显示图片: " + filename);
}
}
class VirtualImageProxy implements Image {
private RealImage realImage;
private String filename;
public VirtualImageProxy(String filename) {
this.filename = filename;
// 构造时不加载图片
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename); // 第一次使用时才加载
}
realImage.display();
}
}2. 权限控制(保护代理)
java
/**
* 数据访问保护代理
* 根据用户角色控制数据的读写权限
*/
interface DataAccess {
String readData(String key);
void writeData(String key, String value);
}
class FileDataAccess implements DataAccess {
private java.util.Map<String, String> data = new java.util.HashMap<>();
@Override
public String readData(String key) {
return data.getOrDefault(key, "无数据");
}
@Override
public void writeData(String key, String value) {
data.put(key, value);
System.out.println(" [文件] 写入: " + key + " = " + value);
}
}
class DataAccessProxy implements DataAccess {
private final DataAccess target;
private final String userRole;
public DataAccessProxy(DataAccess target, String userRole) {
this.target = target;
this.userRole = userRole;
}
@Override
public String readData(String key) {
System.out.println("[权限代理] 检查读取权限...");
if ("admin".equals(userRole) || "user".equals(userRole)) {
return target.readData(key);
}
throw new SecurityException("无读取权限!");
}
@Override
public void writeData(String key, String value) {
System.out.println("[权限代理] 检查写入权限...");
if ("admin".equals(userRole)) {
target.writeData(key, value);
} else {
throw new SecurityException("无写入权限!需要 admin 角色。");
}
}
}十、优缺点分析
优点
- 职责清晰:将核心业务逻辑与横切关注点(日志、安全、缓存)分离
- 开闭原则:增加新的代理功能不需要修改目标类
- 灵活性强:可以在不修改目标类的情况下动态添加功能
- 保护目标对象:客户端不直接访问目标对象,增加了安全性
缺点
- 增加系统复杂度:引入了代理类,增加了系统的类数量
- 性能开销:代理层会带来额外的方法调用开销
- 静态代理不够灵活:每个接口都需要单独编写代理类
十一、使用场景
- 远程代理:为远程对象提供本地代理(如 RPC)
- 虚拟代理:延迟创建开销大的对象
- 保护代理:控制对象的访问权限
- 日志/监控代理:记录方法调用日志
- 缓存代理:缓存方法结果,避免重复计算
- 事务代理:Spring 声明式事务
- AOP:面向切面编程的核心实现
十二、与其他模式的关系
- 装饰器模式 vs 代理模式:装饰器模式关注为对象添加功能,代理模式关注控制对象访问
- 适配器模式 vs 代理模式:适配器改变接口,代理保持接口不变
- 外观模式 vs 代理模式:外观简化接口,代理保持接口但控制访问
总结:代理模式是 Java 中最常用的设计模式之一,尤其在 Spring 框架中广泛应用。选择哪种代理方式取决于是否需要接口、性能要求和项目规范。静态代理适合简单场景,JDK 动态代理适合基于接口的通用增强,CGLIB 适合无接口的类。