【大白话说Java面试题】【Java基础篇】第30题:JDK动态代理和CGLIB动态代理有什么区别

第30题:JDK动态代理和CGLIB动态代理有什么区别

📚 回答:

  • 核心对比
    JDK动态代理和CGLIB动态代理是两种常用的动态代理实现方式,它们在底层原理、使用场景和限制条件上都有显著差异。以下是详细对比:

1. JDK动态代理
  • 定义

    JDK动态代理基于接口实现,要求目标对象必须实现至少一个接口。

  • 实现步骤

    1. 目标类:需要实现一个接口。
    2. 处理类 :实现InvocationHandler接口,编写增强逻辑(如日志记录、权限校验等)。
    3. 生成代理对象 :通过Proxy.newProxyInstance方法动态生成代理对象。

    💡 代码示例

    以下代码展示了JDK动态代理的基本实现:

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Service {
    void execute();
}

class TargetService implements Service {
    @Override
    public void execute() {
        System.out.println("目标对象执行业务逻辑");
    }
}

class DynamicProxyHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理对象:前置处理");
        Object result = method.invoke(target, args);
        System.out.println("代理对象:后置处理");
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        Service target = new TargetService();
        Service proxy = (Service) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new DynamicProxyHandler(target)
        );
        proxy.execute();
    }
}
  • 底层原理

    • Proxy.newProxyInstance方法会根据传入的目标类加载器和接口信息,动态生成一个代理类的字节数组。
    • 使用defineClass0(本地方法)将字节数组加载为代理类实例。
    • 代理类实现了目标接口,代理对象调用接口方法时,会触发InvocationHandlerinvoke方法。
  • 使用场景

    • AOP编程(如Spring中的事务管理)。
    • MyBatis中Mapper接口的动态实现。

2. CGLIB动态代理
  • 定义

    CGLIB(Code Generation Library)基于继承实现,适用于没有实现接口的目标对象。

  • 实现步骤

    1. 目标类 :无需实现接口,但不能是final类或包含final方法。
    2. 处理类 :实现MethodInterceptor接口,编写增强逻辑。
    3. 生成代理对象 :通过Enhancer类动态生成代理对象。

    💡 代码示例

    以下代码展示了CGLIB动态代理的基本实现:

java 复制代码
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

class TargetService {
    public void execute() {
        System.out.println("目标对象执行业务逻辑");
    }
}

class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("代理对象:前置处理");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("代理对象:后置处理");
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(TargetService.class);
        enhancer.setCallback(new CglibProxy());

        TargetService proxy = (TargetService) enhancer.create();
        proxy.execute();
    }
}
  • 底层原理

    • CGLIB通过ASM技术动态生成目标类的子类,并重写目标方法。
    • 当调用代理对象的方法时,实际调用的是子类的重写方法,该方法会触发MethodInterceptorintercept方法。
  • 使用场景

    • Spring框架中对未实现接口的Bean进行AOP代理。
    • Hibernate框架中的延迟加载机制。

3. 对比总结
特性 JDK动态代理 CGLIB动态代理
实现方式 基于接口 基于继承
目标类要求 必须实现接口 无需实现接口,但不能是final
性能 性能略高 性能稍低(生成子类开销较大)
适用场景 目标类实现接口的场景 目标类未实现接口的场景

💡 面试官视角

  • 面试官可能会问"为什么Spring默认优先使用JDK动态代理?"
    答:因为JDK动态代理性能更高,且大多数Spring Bean都会实现接口。
  • 面试官可能会追问"CGLIB有哪些局限性?"
    答:无法代理final类或final方法,因为CGLIB通过继承实现,而final修饰的类或方法无法被继承或重写。

📌 专栏 :大白话说Java面试题 --- 01-Java基础篇

相关推荐
swipe2 小时前
别再把 AI 聊天做成纯文本:从 agui 这个前后端项目,拆解“可感知工具调用”的流式 AI UI
后端·langchain·llm
GetcharZp2 小时前
GitHub 爆火!纯 Go 编写的文件同步神器 Syncthing,凭什么成为程序员的标配?
后端
hERS EOUS2 小时前
SpringBoot 使用 spring.profiles.active 来区分不同环境配置
spring boot·后端·spring
DFT计算杂谈2 小时前
wannier90 参数详解大全
java·前端·css·html·css3
LucianaiB2 小时前
我用飞书多维表做了一个 AI 活动推荐智能体:每天自动催我别错过截止日期!
后端
marsh02062 小时前
43 openclaw熔断与降级:保障系统在异常情况下的可用性
java·运维·网络·ai·编程·技术
张健11564096482 小时前
临界区和同一线程上锁
java·开发语言·jvm
头发够用的程序员2 小时前
C++和Python面试经典算法汇总(一)
开发语言·c++·python·算法·容器·面试
夜猫逐梦2 小时前
【逆向经验】一篇文章讲透为什么CE搜不到Python游戏的内存值
开发语言·python·游戏