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、链路追踪。
相关推荐
腻害兔5 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社6 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
萧瑟余晖7 小时前
JDK 26 新特性详解
java·开发语言
马优晨8 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
维天说10 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json
Zane199411 小时前
并发 vs 并行:别再傻傻分不清了,一文讲透 Java 并发编程的第一课
java·后端
噢,我明白了12 小时前
java中Excel的导入和导出(EasyExcel)
java·开发语言·excel
吠品12 小时前
Zabbix Web界面误报Server未运行的排查与解决
java·服务器·数据库
啊湘12 小时前
天气查询API接口 按月Token鉴权 实时天气 物联网可用 文档齐全
java·后端·struts
Java内核笔记12 小时前
告别十亿美元的错误 : Spring Boot 4 空安全 (JSpecify) 实战
java·spring boot·后端