Java设计模式-代理模式
代理模式是一种结构型设计模式:为 “某个对象” 提供一种 “代理对象” 以控制对它的访问。调用方不直接引用原对象,而是引用代理;代理在“把请求转给原对象的前/后”附加额外逻辑(缓存、鉴权、延迟加载、事务、日志、熔断、AOP 等)。
一、三种实现方式
注:Spring AOP 默认用 JDK 动态代理;若目标无接口则自动切换到 CGLIB。
二、静态代理示例
- 抽象主题:定义业务方法
public interface OrderService { void createOrder(long userId, String itemNo);}
- 真实主题:真正的业务实现(可能部署在远程 / 访问开销很大)
public class OrderServiceImpl implements OrderService { @Override public 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; } @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); }}
- 客户端
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; } @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; }}
- 生成并调用代理
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 { @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; }}
使用
Enhancer enhancer = new Enhancer();enhancer.setSuperclass(OrderServiceImpl.class); // 被代理类enhancer.setCallback(new CglibMetricInterceptor());OrderServiceImpl proxy = (OrderServiceImpl) enhancer.create();proxy.createOrder(1003L, \"C20240522\");
五、常见 UML 类图
+----------------+ ------> | <> | uses | Subject | RealSubject +-------+-------+ / Proxy | | +--------------+-------------+ | | +-----+-------+ +------+------+ | RealSubject | implements | Proxy | +-------------+ +-------------+
六、与其他模式的关系
- 装饰器模式:都通过“组合”包装对象,但装饰器更强调“功能叠加”,而不控制访问。
- 门面模式:门面做“高层封装”,代理做“同层”的替代。
- 桥接模式:两者都解耦抽象与实现,但侧重点不同。
- Spring AOP:本质就是动态代理(JDK / CGLIB)。
七、最佳实践
- 延迟加载 —— Hibernate / MyBatis:只有在第一次调用时才真正去查库。
- 保护代理 —— 权限框架:代理层统一鉴权。
- 远程代理 —— RPC:Stub 封装网络通信。
- 缓存代理 —— 读取前查缓存,写后更新缓存。
- 日志/监控 —— 微服务 Sidecar、链路追踪。