面试必问!JDK动态代理和CGLIB动态代理的核心区别

面试必问!JDK动态代理和CGLIB动态代理的核心区别

那场"代理风波"的开始

上个月的技术面试,当面试官问到"Spring AOP底层用的是什么代理技术"时,我信心满满地回答:"动态代理啊!"然后他又问:"JDK动态代理和CGLIB有什么区别?"

瞬间懵了。虽然平时用Spring框架时代理功能运行得好好的,但要说出具体区别,我还真说不清楚。回家后决定好好研究一下这个"面试杀手"问题。

初探:两个"代理门派"的身世

就像武侠小说里的两大门派,JDK动态代理和CGLIB各有自己的"武功心法":

JDK动态代理:

  • 身世:JDK原生支持,从1.3版本就有了
  • 特点:只能代理实现了接口的类
  • 原理:基于接口实现,生成接口的实现类

CGLIB动态代理:

  • 身世:第三方库,Code Generation Library的缩写
  • 特点:可以代理普通类,不需要接口
  • 原理:基于继承,生成目标类的子类

动手实践:看看它们的"招式"

JDK动态代理的"轻功"

java 复制代码
// 目标接口和实现
interface UserService {
    void saveUser(String name);
}

// JDK动态代理的核心
Object proxyInstance = Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    (proxy, method, args) -> {
        System.out.println("前置处理...");
        Object result = method.invoke(target, args);
        System.out.println("后置处理...");
        return result;
    }
);

CGLIB的"内功心法"

java 复制代码
// 不需要接口,直接代理普通类
public class UserServiceImpl {
    public void saveUser(String name) {
        System.out.println("保存用户: " + name);
    }
}

// CGLIB代理创建
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
    System.out.println("CGLIB前置处理...");
    Object result = proxy.invokeSuper(obj, args);
    System.out.println("CGLIB后置处理...");
    return result;
});
UserServiceImpl proxy = (UserServiceImpl) enhancer.create();

踩坑瞬间:不是所有类都能被"代理"

研究过程中遇到了几个坑:

JDK代理的限制:

  • 目标类必须实现接口,否则直接报错
  • 只能拦截接口中定义的方法

CGLIB的"软肋":

  • final类无法被继承,代理创建失败
  • final方法无法被重写,拦截不到
  • private方法也拦截不到

最让我印象深刻的是,当我试图用CGLIB代理一个final类时:

java 复制代码
public final class FinalService {
    public void doSomething() {
        System.out.println("final类的方法");
    }
}
// 运行时抛出异常:Cannot subclass final class

性能PK:速度与灵活性的较量

通过简单的性能测试发现了有趣的现象:

对比维度 JDK动态代理 CGLIB
代理创建速度 快 慢(需要生成字节码)
方法调用速度 慢(反射调用) 快(直接调用)
内存占用 小 大(生成更多类)
适用场景 接口代理 类代理

这就像是"轻功"与"内功"的区别:JDK代理启动快但调用慢,CGLIB启动慢但调用快。

经验启示:Spring框架的"智慧选择"

研究完才明白,Spring AOP为什么这么"聪明":

  1. 默认策略:目标类实现了接口 → 用JDK代理;否则 → 用CGLIB
  2. 可配置 :通过proxyTargetClass=true强制使用CGLIB
  3. 最佳实践:尽量基于接口编程,让JDK代理发挥作用

实际开发建议:

  • 设计时优先考虑接口,让JDK代理处理大部分场景
  • 对于第三方类或无法修改的类,CGLIB是救星
  • 性能敏感的场景,选择CGLIB(如高频调用的方法)
  • 简单的AOP场景,JDK代理足够了

总结:代理技术的"阴阳平衡"

JDK动态代理和CGLIB就像技术世界的"阴阳两极",各有所长:

  • JDK代理:轻量、原生、基于接口,适合规范的面向对象设计
  • CGLIB:强大、灵活、基于继承,能处理更复杂的代理需求

在面试中,关键不是死记硬背区别,而是理解它们的设计思想和适用场景。毕竟,没有最好的技术,只有最合适的选择。

现在再遇到这个面试题,我可以从容地说出:"这两种代理技术在不同场景下各有优势,Spring框架的智能选择策略正是这种技术平衡的体现。"

本文转自渣哥zha-ge.cn/java/3

相关推荐
Wang's Blog12 分钟前
Java 项目实战:交友项目 - 联系人列表
java·开发语言·交友
我命由我1234516 分钟前
访客系统形式(有用户概念、无用户概念)
java·开发语言·前端·javascript·python·产品运营·产品经理
香蕉大香蕉(06敬畏市场)1 小时前
MySQL 单点详解(01)
java·数据库·mysql·面试·博客·实习
LaughingZhu1 小时前
Product Hunt 热榜 2026-10-30:Empacer、Scape、Floot in ChatGPT
java·前端·数据库·人工智能·chatgpt·notion
niucloud-admin1 小时前
NIUCLOUD PHP SAAS上门家政使用文档:服务保障
java·开发语言·php
IT枫斗者枫哥2 小时前
CompletableFuture失败后返回空列表:异常没了,业务含义也变了
java·后端
知识搬运临时工2 小时前
一个模板把 AEM 拖进 Full GC 死亡螺旋:300% CPU、7 GB 堆、310 万行堆栈
java·aws
用户432935928052 小时前
CoAP还是MQTT:1万只Cat.1水表三个月带宽续航实测
java
知守观2 小时前
多模块架构演进:横向分层 vs 纵向领域,13 个真实项目的切分对照
java·后端·微服务
Java成神之路-2 小时前
Java String 为什么不可变?和 HashMap 到底有什么关系?
java