代理模式是一种结构型设计模式:为 "某个对象" 提供一种 "代理对象" 以控制对它的访问。调用方不直接引用原对象,而是引用代理;代理在"把请求转给原对象的前/后"附加额外逻辑(缓存、鉴权、延迟加载、事务、日志、熔断、AOP 等)。
一、三种实现方式
实现方式 | 原理与场景 | 优点 | 缺点 |
---|---|---|---|
静态代理 | 为每一个被代理类 手工编写 一个代理类,编译期就确定代理逻辑 | 简单,易调试 | 代理类爆炸,难维护 |
JDK 动态代理 | JDK 反射 运行时生成 代理类,要求被代理类 必须实现接口 | 无侵入,代码量小,官方支持 | 只能代理接口 |
CGLIB 动态代理 | ASM 字节码库 运行时生成 被代理类的子类,覆盖方法 | 能代理类(无接口也行) | 不能代理 final 类/方法 |
注:Spring AOP 默认用 JDK 动态代理;若目标无接口则自动切换到 CGLIB。
二、静态代理示例
- 抽象主题:定义业务方法
java
public interface OrderService {
void createOrder(long userId, String itemNo);
}
- 真实主题:真正的业务实现(可能部署在远程 / 访问开销很大)
java
public class OrderServiceImpl implements OrderService {
@Override
public void createOrder(long userId, String itemNo) {
// 业务:落库、扣库存、推送消息 ...
System.out.printf("为用户 %d 创建订单,商品 %s%n", userId, itemNo);
}
}
- 代理:给 createOrder 前后加"鉴权 + 耗时统计"
java
public class OrderServiceProxy implements OrderService {
private final OrderService target; // 被代理对象
public OrderServiceProxy(OrderService target) {
this.target = target;
}
@Override
public void createOrder(long userId, String itemNo) {
preCheck(userId); // 前置增强
long s = System.nanoTime();
target.createOrder(userId, itemNo); // 真正的业务
long cost = System.nanoTime() - s;
System.out.println(" ==> 耗时(ms) = " + cost / 1_000_000.0);
}
private void preCheck(long userId) {
System.out.println("鉴权... 用户ID = " + userId);
}
}
- 客户端
java
OrderService real = new OrderServiceImpl();
OrderService proxy = new OrderServiceProxy(real);
proxy.createOrder(1001L, "A20240522");
输出
java
鉴权... 用户ID = 1001
为用户 1001 创建订单,商品 A20240522
==> 耗时(ms) = 0.52
缺点:接口一多,就要编写 N 个 XxxServiceProxy
,后期难以维护。
三、JDK 动态代理示例
核心类:java.lang.reflect.Proxy
+ InvocationHandler
- 编写 InvocationHandler
java
public class MetricInvocationHandler implements InvocationHandler {
private final Object target; // 真正业务对象
public MetricInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long s = System.nanoTime();
Object ret = method.invoke(target, args);
System.out.printf("方法 %s 执行耗时(ms): %.2f%n",
method.getName(), (System.nanoTime() - s) / 1_000_000.0);
return ret;
}
}
- 生成并调用代理
java
OrderService real = new OrderServiceImpl();
OrderService proxy = (OrderService) Proxy.newProxyInstance(
OrderService.class.getClassLoader(), // 类加载器
new Class<?>[]{OrderService.class}, // 需要实现的接口
new MetricInvocationHandler(real));
proxy.createOrder(1002L, "B20240522");
输出
java
为用户 1002 创建订单,商品 B20240522
方法 createOrder 执行耗时(ms): 0.30
注:JDK 动态代理只能在 运行时 生成代理对象,无法代理类本身的方法(如
OrderServiceImpl
中未定义在接口的方法)。
四、CGLIB 动态代理示例
引用库:cglib 或 spring-core(已含 cglib)
java
public class CglibMetricInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
long s = System.nanoTime();
Object ret = proxy.invokeSuper(obj, args); // 调用父类(真实类)方法
System.out.printf("CGLIB: %s 耗时(ms) %.2f%n",
method.getName(), (System.nanoTime() - s) / 1_000_000.0);
return ret;
}
}
使用
java
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderServiceImpl.class); // 被代理类
enhancer.setCallback(new CglibMetricInterceptor());
OrderServiceImpl proxy = (OrderServiceImpl) enhancer.create();
proxy.createOrder(1003L, "C20240522");
五、常见 UML 类图
+----------------+
------> | <<interface>> |
uses | Subject |
RealSubject +-------+-------+
/ Proxy |
|
+--------------+-------------+
| |
+-----+-------+ +------+------+
| RealSubject | implements | Proxy |
+-------------+ +-------------+
六、与其他模式的关系
- 装饰器模式:都通过"组合"包装对象,但装饰器更强调"功能叠加",而不控制访问。
- 门面模式:门面做"高层封装",代理做"同层"的替代。
- 桥接模式:两者都解耦抽象与实现,但侧重点不同。
- Spring AOP:本质就是动态代理(JDK / CGLIB)。
七、最佳实践
- 延迟加载 ------ Hibernate / MyBatis:只有在第一次调用时才真正去查库。
- 保护代理 ------ 权限框架:代理层统一鉴权。
- 远程代理 ------ RPC:Stub 封装网络通信。
- 缓存代理 ------ 读取前查缓存,写后更新缓存。
- 日志/监控 ------ 微服务 Sidecar、链路追踪。