面试必问!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

相关推荐
该用户已不存在1 分钟前
Gradle vs. Maven,Java 构建工具该用哪个?
java·后端·maven
浮游本尊16 分钟前
Java学习第11天 - Spring Boot高级特性与实战项目
java
浮游本尊16 分钟前
Java学习第12天 - Spring Security安全框架与JWT认证
java
David爱编程35 分钟前
happens-before 规则详解:JMM 中的有序性保障
java·后端
小张学习之旅36 分钟前
ConcurrentHashMap
java·后端
熊文豪1 小时前
保姆级Maven安装与配置教程(Windows版)
java·windows·maven·maven安装教程·maven配置教程·maven安装与配置教程
怀旧,2 小时前
【C++】 9. vector
java·c++·算法
渣哥2 小时前
震惊!Java注解背后的实现原理,竟然如此简单又高深!
java
hqxstudying2 小时前
JAVA限流方法
java·开发语言·安全·限流
shylyly_2 小时前
Linux->多线程2
java·linux·多线程·线程安全·线程同步·线程互斥·可重入