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、链路追踪。
相关推荐
金銀銅鐵4 分钟前
[Java] 如何自动生成简单的 PlantUML 类图
java·后端
Edward111111118 分钟前
3月23Math类,Arrays类
java·学习
小江的记录本8 分钟前
【Spring Boot】Spring Boot 全体系知识结构化拆解(附 Spring Boot 高频面试八股文精简版)
java·spring boot·后端·spring·面试·tomcat·mybatis
Thomas.Sir12 分钟前
从底层源码深入剖析 MyBatis 工作原理
java·架构·mybatis
九天轩辕13 分钟前
Android CI/CD 编译 AIDL 报错分析与解决
android·java·ci/cd
码农42718 分钟前
点评项目深入改造-------日常学习笔记
java·笔记·学习·搜索引擎·全文检索
Ivanqhz19 分钟前
寄存器分配的核心函数 allocate
java·开发语言·后端·python·rust
爱吃烤鸡翅的酸菜鱼20 分钟前
Spring Cloud Eureka 服务注册与发现实战详解:从原理到高可用集群搭建
java·spring·spring cloud·eureka
野犬寒鸦24 分钟前
JVM垃圾回收机制深度解析(G1篇)(垃圾回收过程及专业名词详解)(补充)
java·服务器·开发语言·jvm·后端·面试
白宇横流学长25 分钟前
基于SpringBoot实现的信息技术知识赛系统设计与实现【源码+文档】
java·spring boot·后端