Java设计模式-代理模式
代理模式是一种结构型设计模式:为 “某个对象” 提供一种 “代理对象” 以控制对它的访问。调用方不直接引用原对象,而是引用代理;代理在“把请求转给原对象的前/后”附加额外逻辑(缓存、鉴权、延迟加载、事务、日志、熔断、AOP 等)。
一、三种实现方式
实现方式 | 原理与场景 | 优点 | 缺点 |
---|---|---|---|
静态代理 | 为每一个被代理类 手工编写 一个代理类,编译期就确定代理逻辑 | 简单,易调试 | 代理类爆炸,难维护 |
JDK 动态代理 | JDK 反射 运行时生成 代理类,要求被代理类 必须实现接口 | 无侵入,代码量小,官方支持 | 只能代理接口 |
CGLIB 动态代理 | ASM 字节码库 运行时生成 被代理类的子类,覆盖方法 | 能代理类(无接口也行) | 不能代理 final 类/方法 |
注:Spring AOP 默认用 JDK 动态代理;若目标无接口则自动切换到 CGLIB。
二、静态代理示例
- 抽象主题:定义业务方法
public interface OrderService {void createOrder(long userId, String itemNo);
}
- 真实主题:真正的业务实现(可能部署在远程 / 访问开销很大)
public class OrderServiceImpl implements OrderService {@Overridepublic void createOrder(long userId, String itemNo) {// 业务:落库、扣库存、推送消息 …System.out.printf("为用户 %d 创建订单,商品 %s%n", userId, itemNo);}
}
- 代理:给 createOrder 前后加“鉴权 + 耗时统计”
public class OrderServiceProxy implements OrderService {private final OrderService target; // 被代理对象public OrderServiceProxy(OrderService target) {this.target = target;}@Overridepublic 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);}
}
- 客户端
OrderService real = new OrderServiceImpl();
OrderService proxy = new OrderServiceProxy(real);
proxy.createOrder(1001L, "A20240522");
输出
鉴权… 用户ID = 1001
为用户 1001 创建订单,商品 A20240522==> 耗时(ms) = 0.52
缺点:接口一多,就要编写 N 个 XxxServiceProxy
,后期难以维护。
三、JDK 动态代理示例
核心类:java.lang.reflect.Proxy
+ InvocationHandler
- 编写 InvocationHandler
public class MetricInvocationHandler implements InvocationHandler {private final Object target; // 真正业务对象public MetricInvocationHandler(Object target) {this.target = target;}@Overridepublic 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;}
}
- 生成并调用代理
OrderService real = new OrderServiceImpl();
OrderService proxy = (OrderService) Proxy.newProxyInstance(OrderService.class.getClassLoader(), // 类加载器new Class<?>[]{OrderService.class}, // 需要实现的接口new MetricInvocationHandler(real));proxy.createOrder(1002L, "B20240522");
输出
为用户 1002 创建订单,商品 B20240522
方法 createOrder 执行耗时(ms): 0.30
注:JDK 动态代理只能在 运行时 生成代理对象,无法代理类本身的方法(如
OrderServiceImpl
中未定义在接口的方法)。
四、CGLIB 动态代理示例
引用库:cglib 或 spring-core(已含 cglib)
public class CglibMetricInterceptor implements MethodInterceptor {@Overridepublic 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;}
}
使用
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、链路追踪。