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

Java 动态代理:JDK 与 CGLIB 实现与取舍

动态代理是 Java 中实现横切逻辑的关键机制。JDK 动态代理基于接口,轻量无依赖,调用效率高;CGLIB 通过子类生成,无需接口但不能代理 final 类。结合日志增强案例对比两者实现,分析性能特点与 Spring AOP 中的默认选择,帮助开发者在实际项目中做出合理取舍。

CloudNative发布于 2026/6/16更新于 2026/10/1961 浏览
Java 动态代理:JDK 与 CGLIB 实现与取舍

动态代理是 Java 里一个很实用的机制——你不用为每个类单独写代理,就能给一堆方法织入横切逻辑。Spring AOP 背后靠的就是它。不过,Java 原生提供了一种动态代理方案,但还有第三方的 CGLIB 常被用到,两者思路不同,场景也不一样。

技术图解

简单说,动态代理是在运行时生成代理类,而不是编译前写死的。这里面有三个角色:目标类、代理类和增强逻辑(Advice)。你调代理的方法,它会在中间插一脚,执行增强逻辑后再调目标方法。

架构示意

JDK 动态代理要求目标类必须实现接口。它用 Proxy 和 InvocationHandler 生成一个实现了那些接口的代理类,方法调用都会被转发到 invoke 方法里。你在 invoke 里可以写日志、权限检查,然后再调用目标方法。

流程演示

下面是一个用日志增强的完整例子:

// 1. 定义接口
public interface UserService {
    void addUser(String name);
}

// 2. 实现目标类
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("添加用户:" + name);
    }
}

// 3. 实现 InvocationHandler(增强逻辑)
public class LogInvocationHandler implements InvocationHandler {
    private Object target; // 目标类引用

    public LogInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法" + method.getName() + "开始执行,参数:" + Arrays.toString(args));
        Object result = method.invoke(target, args);
        System.out.println("方法" + method.getName() + "执行结束");
        return result;
    }
}

// 4. 生成代理类并测试
public class JdkProxyTest {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new LogInvocationHandler(target)
        );
        proxy.addUser("张三");
    }
}

JDK 代理的好处是不用第三方库,生成代理快,调用效率也高。缺点就是必须要有接口,早期项目里有的类没抽象出接口,这就用不了。

JDK 原理

CGLIB 正好弥补了这个缺口。它通过生成目标类的子类来做代理,所以不需要接口。你只要在 MethodInterceptor 的 intercept 方法里写增强逻辑就行。

引入依赖:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

示例:

// 1. 目标类(无需接口)
public class OrderService {
    public void createOrder(String orderId) {
        System.out.println("创建订单:" + orderId);
    }
}

// 2. 实现 MethodInterceptor(增强逻辑)
public class LogMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("方法" + method.getName() + "开始执行,参数:" + Arrays.toString(args));
        Object result = methodProxy.invokeSuper(o, args);
        System.out.println("方法" + method.getName() + "执行结束");
        return result;
    }
}

// 3. 生成代理类并测试
public class CglibProxyTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderService.class);
        enhancer.setCallback(new LogMethodInterceptor());
        OrderService proxy = (OrderService) enhancer.create();
        proxy.createOrder("ORDER_001");
    }
}

CGLIB 不需要接口,但无法代理 final 类或 final 方法,因为不能生成子类或覆写。另外它要依赖第三方库,生成代理类时操作字节码会慢一点,调用效率也稍低——不过 JDK 8 以后差距已经很小了。

实际中该怎么选?Spring AOP 的决策逻辑很清晰:如果目标类实现了接口,默认用 JDK 代理;否则用 CGLIB。这也是一般项目里最省心的做法。如果你自己写工具类,目标类有接口,我建议优先选 JDK 原生方案,零依赖且性能更好;如果迫不得已要代理一个没有接口的类,那就只能上 CGLIB。

搞懂这两者的原理,除了应付面试,更多是让你在线上排查代理失效问题(比如为什么事务没生效?)时能立刻反应过来:是不是类内部调用绕过了代理。

更多推荐文章

查看全部
  • Python 核心知识点速查:31 个基础要点
  • Open-Lovable 网页克隆工具本地部署与远程访问方案
  • AI 开发工具与学习资源指南:涵盖视频、文案及模型应用
  • Function Call、Skill 和 MCP:AI 工具链的三层抽象
  • Flutter 三方库 shelf_modular 的鸿蒙化适配指南
  • 贪心算法实战:从摆动序列到股票买卖
  • MySQL 深入理解 InnoDB 核心文件结构:.ibd、.ib_logfile、undo 日志与 ibdata1
  • 使用 TRAE CN 与 MasterGo MCP 实现设计稿转前端代码
  • CS144 课程 C++ 核心知识点总结
  • 自然科学领域机器学习与深度学习:从数据预处理到时空建模
  • KiCad 符号编辑器引脚添加与同步机制解析
  • Spring Boot 中 MultipartFile 转 File 对象的四种实现方案
  • ChatGLM-6B 大模型快速部署实战指南
  • OpenClaw 结合 Qwen3.5 实现本地 AI 助手部署
  • Flutter wasm_ffi 在鸿蒙端的 WebAssembly 集成与适配实战
  • AI 工具提升产品经理工作效率实战指南
  • MATLAB 实现基于多目标粒子群算法(MOPSO)的无人机三维路径规划
  • Java 填充 Word 模板实战:文本、列表与复选框处理
  • 嵌入式开发中 C/C++ 核心知识点梳理
  • Spring Boot 全局异常处理与日志监控实战

相关免费在线工具

  • 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