跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Java 代理模式完全指南:从静态到动态

深入讲解 Java 代理模式,涵盖静态代理与动态代理的核心原理及实现。静态代理需手动编写代理类,耦合度高;动态代理在运行时生成字节码,分为基于接口的 JDK 动态代理和基于继承的 CGLIB 动态代理。文章通过 PaymentService 和 DataQueryService 等完整代码示例,演示了 InvocationHandler 和 MethodInterceptor 的使用流程,对比了两种动态代理的实现差异、性能表现及适用场景,并给出了 Spring AOP 中的实际应用建议。掌握代理模式是理解 Spring AOP、RPC 框架的基础。

剑仙发布于 2026/3/28更新于 2026/10/24121 浏览

一、理解代理模式本质

代理模式的核心思想是引入一个中间层——即代理对象,让它代替真实对象处理外部请求。这样做的好处是:无需改动原有代码,就能在目标方法执行前后插入额外逻辑。

生活化比喻:想象你是一位明星(真实对象),所有粉丝来信都由经纪人(代理对象)先过目筛选。经纪人可以在转交信件前进行内容审查、分类整理,甚至直接拒绝某些来信,而你只需要处理经过筛选后的邮件。

代理模式的价值:

  • 解耦:调用方无需直接依赖目标对象
  • 增强:在不修改源码的前提下扩展功能
  • 控制:对访问权限、执行流程进行管控

二、静态代理:手动编码的代理方式

2.1 核心特点

静态代理要求开发者手工编写代理类,在编译阶段代理类的代码就已完全确定。从 JVM 视角看,编译后会产出三个独立的 .class 文件:接口、实现类、代理类。

主要缺陷:

  • 高度耦合:接口每新增一个方法,代理类必须同步修改
  • 重复劳动:N 个目标类需要编写 N 个代理类
  • 维护困难:业务复杂时代码量呈爆炸式增长
2.2 完整实现示例

步骤一:定义业务接口

public interface MessageService { 
    String deliver(String content); 
}

步骤二:实现真实业务

public class MessageServiceImpl implements MessageService { 
    @Override 
    public String deliver(String content) { 
        System.out.println("正在发送消息:" + content); 
        return content; 
    } 
}

步骤三:手工编写代理类

public class MessageServiceProxy implements MessageService { 
    private final MessageService realService;
    
    public MessageServiceProxy(MessageService service) { 
        this.realService = service; 
    }
    
    @Override 
    public String deliver(String content) { 
        // 前置处理
        System.out.println("[代理] 开始处理消息,内容长度:" + content.length()); 
        // 调用真实对象
        String result = realService.deliver(content); 
        // 后置处理
        System.out.println("[代理] 消息处理完成,返回结果:" + result); 
        return result; 
    } 
}

步骤四:客户端调用

public class Client { 
    public static void main(String[] args) { 
        MessageService real = new MessageServiceImpl(); 
        MessageService proxy = new MessageServiceProxy(real); 
        proxy.deliver("Hello, Proxy Pattern!"); 
    } 
}

执行结果:

[代理] 开始处理消息,内容长度:22
正在发送消息:Hello, Proxy Pattern!
[代理] 消息处理完成,返回结果:Hello, Proxy Pattern!

三、动态代理:运行时生成代理类

动态代理将代理类的生成时机推迟到程序运行阶段,JVM 会在内存中动态构建代理类的字节码并加载。这彻底解决了静态代理"一个目标类对应一个代理类"的痛点。

技术价值:

  • Spring AOP 的底层基石
  • RPC 框架(如 Dubbo)的核心实现
  • 事务管理、日志记录、权限校验的通用解决方案

Java 生态中主流的动态代理技术分为 JDK 内置实现 和 CGLIB 第三方库 两种。


四、JDK 动态代理:官方标准方案

4.1 技术架构

JDK 动态代理依赖两个核心组件:

组件类型职责
java.lang.reflect.Proxy类工厂类,负责生成代理对象
java.lang.reflect.InvocationHandler接口回调接口,定义方法拦截逻辑

Proxy.newProxyInstance() 方法签名:

public static Object newProxyInstance( 
    ClassLoader loader,      // 类加载器:用于加载动态生成的代理类 
    Class<?>[] interfaces,   // 接口数组:代理类需要实现的接口列表 
    InvocationHandler h      // 处理器:方法调用的实际处理者 
)

InvocationHandler.invoke() 方法:

public Object invoke( 
    Object proxy,    // 代理对象实例(慎用,避免循环调用) 
    Method method,   // 被调用的方法反射对象 
    Object[] args    // 方法参数数组 
)
4.2 实现流程

第一步:定义业务契约与实现

// 接口(JDK 动态代理必须基于接口)
public interface PaymentService { 
    boolean pay(String orderId, BigDecimal amount); 
}

// 真实实现
public class PaymentServiceImpl implements PaymentService { 
    @Override 
    public boolean pay(String orderId, BigDecimal amount) { 
        System.out.printf("执行支付:订单 [%s], 金额 [¥%s]%n", orderId, amount); 
        return true; 
    } 
}

第二步:实现调用处理器

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

public class PerformanceInvocationHandler implements InvocationHandler { 
    private final Object targetObject;
    
    public PerformanceInvocationHandler(Object target) { 
        this.targetObject = target; 
    }
    
    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
        long startTime = System.currentTimeMillis(); 
        System.out.printf("[监控] 方法:%s, 参数:%s%n", method.getName(), Arrays.toString(args)); 
        // 通过反射调用目标对象的真实方法
        Object result = method.invoke(targetObject, args); 
        long cost = System.currentTimeMillis() - startTime; 
        System.out.printf("[监控] 执行耗时:%dms, 返回值:%s%n", cost, result); 
        return result; 
    } 
}

第三步:创建代理工厂

import java.lang.reflect.Proxy;

public class ServiceProxyFactory { 
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) { 
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(), 
            target.getClass().getInterfaces(), 
            new PerformanceInvocationHandler(target)
        ); 
    } 
}

第四步:业务调用

public class Application { 
    public static void main(String[] args) { 
        PaymentService realService = new PaymentServiceImpl(); 
        PaymentService proxy = ServiceProxyFactory.createProxy(realService); 
        proxy.pay("ORD-2024-001", new BigDecimal("199.99")); 
    } 
}

输出:

[监控] 方法:pay, 参数:[ORD-2024-001, 199.99]
执行支付:订单 [ORD-2024-001], 金额 [¥199.99]
[监控] 执行耗时:12ms, 返回值:true

五、CGLIB 动态代理:无接口的继承方案

5.1 技术背景

JDK 动态代理有一个先天限制:只能代理实现了接口的类。对于没有接口的遗留类或第三方类,CGLIB(Code Generation Library)提供了基于继承的解决方案。

核心机制:

  • 在运行时动态生成目标类的子类
  • 通过方法重写(Override)插入增强逻辑
  • 使用 ASM 字节码操作库生成类

关键组件:

组件类型职责
net.sf.cglib.proxy.Enhancer类配置并生成代理类
net.sf.cglib.proxy.MethodInterceptor接口定义方法拦截逻辑
net.sf.cglib.proxy.MethodProxy类高性能方法调用(避免反射)
5.2 依赖配置
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
5.3 完整实现

场景:为无接口的类添加缓存功能

第一步:定义目标类(无接口)

public class DataQueryService { 
    // 模拟耗时查询
    public String queryFromDatabase(String sql) { 
        System.out.println("执行数据库查询:" + sql); 
        try { 
            Thread.sleep(100); // 模拟耗时
        } catch (InterruptedException e) { 
            Thread.currentThread().interrupt(); 
        } 
        return "Result of: " + sql; 
    }
    
    public final String querySystemConfig(String key) { 
        // final 方法无法被代理
        return "SystemConfig:" + key; 
    } 
}

第二步:实现方法拦截器

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CacheMethodInterceptor implements MethodInterceptor { 
    private final Map<String, Object> cache = new HashMap<>();
    
    @Override 
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
        String cacheKey = method.getName() + ":" + args[0]; 
        // 检查缓存
        if (cache.containsKey(cacheKey)) { 
            System.out.println("[缓存] 命中:" + cacheKey); 
            return cache.get(cacheKey); 
        }
        // 执行真实方法(使用 MethodProxy 避免反射开销)
        System.out.println("[缓存] 未命中,执行真实方法:" + method.getName()); 
        Object result = proxy.invokeSuper(obj, args); 
        // 存入缓存
        cache.put(cacheKey, result); 
        System.out.println("[缓存] 已存储:" + cacheKey); 
        return result; 
    } 
}

第三步:创建代理生成器

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyGenerator { 
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(Class<T> targetClass) { 
        Enhancer enhancer = new Enhancer(); 
        // 设置父类(被代理的类)
        enhancer.setSuperclass(targetClass); 
        // 设置回调(方法拦截器)
        enhancer.setCallback(new CacheMethodInterceptor()); 
        // 创建代理实例
        return (T) enhancer.create(); 
    } 
}

第四步:客户端调用

public class CglibDemo { 
    public static void main(String[] args) { 
        DataQueryService proxy = CglibProxyGenerator.createProxy(DataQueryService.class); 
        // 第一次调用:执行真实方法
        String result1 = proxy.queryFromDatabase("SELECT * FROM users"); 
        System.out.println("结果 1: " + result1); 
        System.out.println("---"); 
        // 第二次调用:命中缓存
        String result2 = proxy.queryFromDatabase("SELECT * FROM users"); 
        System.out.println("结果 2: " + result2); 
        // final 方法无法被代理,直接调用父类实现
        System.out.println("---"); 
        String config = proxy.querySystemConfig("timeout"); 
        System.out.println("配置:" + config); 
    } 
}

输出结果:

[缓存] 未命中,执行真实方法:queryFromDatabase
执行数据库查询:SELECT * FROM users
[缓存] 已存储:queryFromDatabase:SELECT * FROM users
结果 1: Result of: SELECT * FROM users
---
[缓存] 命中:queryFromDatabase:SELECT * FROM users
结果 2: Result of: SELECT * FROM users
---
配置:SystemConfig:timeout

重要提示:MethodProxy.invokeSuper() 是 CGLIB 的关键,它通过 FastClass 机制直接调用父类方法,避免了反射的性能损耗。如果误用 method.invoke(obj, args) 会导致循环调用(因为 obj 是代理对象,会再次触发拦截器)。


六、两种动态代理深度对比

6.1 技术实现差异
对比维度JDK 动态代理CGLIB 动态代理
实现原理实现接口(生成接口的实现类)继承类(生成目标类的子类)
强制要求目标类必须实现接口目标类不能是 final 类
方法限制接口定义的所有方法非 final、非 private 方法
核心类Proxy、InvocationHandlerEnhancer、MethodInterceptor
方法调用Java 反射(相对较慢)FastClass 机制(方法索引,更快)
包归属JDK 内置(java.lang.reflect)第三方库(net.sf.cglib)
生成类名com.sun.proxy.$Proxy0Target$$EnhancerByCGLIB$$hash
6.2 性能与适用场景

性能表现:

  • JDK 动态代理:随着 JDK 版本升级(尤其是 JDK 8 之后),反射性能已大幅优化,在大多数场景下与 CGLIB 差距不大
  • CGLIB:首次生成代理类较慢(需要生成字节码),但调用阶段通过 FastClass 机制略快

选型建议:

if (目标类实现了接口) {
    // 优先使用 JDK 动态代理(Spring 默认策略)
} else {
    // 使用 CGLIB 动态代理
}
// Spring 强制使用 CGLIB 的配置
@EnableAspectJAutoProxy(proxyTargetClass = true)

七、静态代理与动态代理全局对比

特性静态代理动态代理
代理类生成时机编译期(预先编写 .java 文件)运行期(内存中生成字节码)
实现方式手动编写代理类,需与目标类实现同一接口,一对一绑定无需手动编写代理类,通过 Handler/Interceptor 封装增强逻辑,一对多复用
代码维护成本高(接口变更需同步修改代理类)低(与接口解耦,逻辑复用)
灵活性低(一对一绑定)高(一个处理器服务多个目标类)
编码工作量代码量大(目标类越多,代理类越多),维护成本高;接口新增方法时,目标类与代理类需同步修改代码量极少(通用增强逻辑可复用),维护性好;与接口解耦,接口变更不影响代理逻辑
学习曲线平缓(纯 Java 语法)较陡(需理解反射/字节码)
典型应用设计模式学习、简单装饰场景框架开发(AOP、RPC、ORM)

八、总结与实践建议

8.1 知识要点回顾
  1. 代理模式本质:通过中间层控制访问,实现功能增强
  2. 静态代理:手工编码,编译期确定,适合简单固定场景
  3. JDK 动态代理:基于接口,使用 Proxy + InvocationHandler,官方标准方案
  4. CGLIB 动态代理:基于继承,使用 Enhancer + MethodInterceptor,解决无接口类的代理问题
8.2 实际开发建议
  • 日常业务开发:直接使用 Spring AOP(底层已封装两种代理,自动选择)
  • 框架工具开发:根据目标类特性选择 JDK 或 CGLIB 原生 API
  • 性能敏感场景:考虑使用字节码生成库(如 ByteBuddy、Javassist)获得更细粒度控制
  • 调试技巧:设置系统属性保存生成的代理类文件,便于分析排查问题
// 保存 JDK 动态代理类
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 保存 CGLIB 代理类
System.setProperty("cglib.debugLocation", "/path/to/output");

掌握代理模式不仅是理解 Spring AOP、MyBatis、Dubbo 等框架的基础,更是写出高扩展性、低耦合代码的重要技能。建议通过手写 RPC 框架或简易 AOP 来加深理解。

目录

  1. 一、理解代理模式本质
  2. 二、静态代理:手动编码的代理方式
  3. 2.1 核心特点
  4. 2.2 完整实现示例
  5. 三、动态代理:运行时生成代理类
  6. 四、JDK 动态代理:官方标准方案
  7. 4.1 技术架构
  8. 4.2 实现流程
  9. 五、CGLIB 动态代理:无接口的继承方案
  10. 5.1 技术背景
  11. 5.2 依赖配置
  12. 5.3 完整实现
  13. 六、两种动态代理深度对比
  14. 6.1 技术实现差异
  15. 6.2 性能与适用场景
  16. 七、静态代理与动态代理全局对比
  17. 八、总结与实践建议
  18. 8.1 知识要点回顾
  19. 8.2 实际开发建议

更多推荐文章

查看全部
  • STM32 嵌入式系统设计:传感器采集与低功耗通信实战
  • DeepFace 结合 OpenCV 实现实时情绪分析系统
  • Whisper Large-V3-Turbo 语音识别技术解析与落地
  • C/C++ 信号与槽机制:原理、实现与工程实践
  • OpenClaw:AI 行动时代的破局者与核心架构解析
  • 开源大模型重塑企业 AI 应用:16 个落地案例解析
  • 零基础转行网络安全就业前景与技能要求分析
  • GitHub 7 大 Claude Skills 开源项目:Skill Creator、Superpowers 与 Code Review 实战指南
  • MySQL 数据类型深度解析:选对类型提升性能
  • Python 面向对象编程三大特性:封装、继承与多态的 15 道实战练习题
  • 节点小宝 4.0 实测:告别网管式运维,打造极简远程工作流
  • C++ 搜索引擎实战:日志宏封装与 Server 交互逻辑
  • CMake与Abseil整合:现代C++工程依赖管理实践
  • 互联网 Android 应用与车载系统开发对比及前景分析
  • Android 离线语音识别 (STT) 高效集成与性能优化
  • FPGA 实现 CIC 抽取滤波器
  • Unity VR 眼镜端高分辨率全景视频播放性能优化
  • Python 制作 Flappy Bird 游戏源码深度解析
  • 基于 DeepFace 和 OpenCV 的情绪分析器实现
  • Python 二级考试真题及参考代码解析(简单应用题)

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online