Java设计模式-代理模式

代理模式是一种结构型设计模式:为 "某个对象" 提供一种 "代理对象" 以控制对它的访问。调用方不直接引用原对象,而是引用代理;代理在"把请求转给原对象的前/后"附加额外逻辑(缓存、鉴权、延迟加载、事务、日志、熔断、AOP 等)。

一、三种实现方式

实现方式 原理与场景 优点 缺点
静态代理 为每一个被代理类 手工编写 一个代理类,编译期就确定代理逻辑 简单,易调试 代理类爆炸,难维护
JDK 动态代理 JDK 反射 运行时生成 代理类,要求被代理类 必须实现接口 无侵入,代码量小,官方支持 只能代理接口
CGLIB 动态代理 ASM 字节码库 运行时生成 被代理类的子类,覆盖方法 能代理类(无接口也行) 不能代理 final 类/方法

注:Spring AOP 默认用 JDK 动态代理;若目标无接口则自动切换到 CGLIB。

二、静态代理示例

  1. 抽象主题:定义业务方法
java 复制代码
public interface OrderService {
    void createOrder(long userId, String itemNo);
}
  1. 真实主题:真正的业务实现(可能部署在远程 / 访问开销很大)
java 复制代码
public class OrderServiceImpl implements OrderService {
    @Override
    public void createOrder(long userId, String itemNo) {
        // 业务:落库、扣库存、推送消息 ...
        System.out.printf("为用户 %d 创建订单,商品 %s%n", userId, itemNo);
    }
}
  1. 代理:给 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);
    }
}
  1. 客户端
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

  1. 编写 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;
    }
}
  1. 生成并调用代理
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)。

七、最佳实践

  1. 延迟加载 ------ Hibernate / MyBatis:只有在第一次调用时才真正去查库。
  2. 保护代理 ------ 权限框架:代理层统一鉴权。
  3. 远程代理 ------ RPC:Stub 封装网络通信。
  4. 缓存代理 ------ 读取前查缓存,写后更新缓存。
  5. 日志/监控 ------ 微服务 Sidecar、链路追踪。
相关推荐
SL_staff11 分钟前
离散制造排程破局:JVS-APS 四步闭环落地实践(开发者视角)
java·产品·全栈
这个昵称不容易21 分钟前
SpringBoot 美容院管理系统:232个Java文件315个接口,5套角色从预约到发货一整条链路
java·springboot·毕设
装不满的克莱因瓶1 小时前
【SpringAI Alibaba】ReactAgent记忆管理MessageTrimmingHook与MessageDeletionHook
java·人工智能·ai·ai编程·springai·springaialibaba
文丑颜不良啊1 小时前
LeetCode2241~2260题目解析
java·leetcode
文丑颜不良啊1 小时前
LeetCode2321~2340题目解析
java·leetcode
驭渊的小故事2 小时前
Spring AOP 核心概念与通知类型详解
java·后端·spring
vipxieliang2 小时前
ValidX vs 手写验证代码:开发效率对比实测
java·spring boot·后端
vipxieliang2 小时前
虚拟线程与 GraalVM:Spring Boot 3.x 云原生性能跃迁
java·spring boot·后端
Rain的Java大神之路2 小时前
面试官最烦的答案:“我用 Redis + Token”!接口幂等 8 种方案 + 四层防御,把重复扣款工单干到 0
java·后端·面试
Rainmicro3 小时前
【无标题】
java·数据库·算法