JDK 动态代理 vs CGLIB:原理、区别与实践指南

在 Java 开发中,动态代理是一种常见且强大的机制,广泛应用于 AOP、RPC、事务控制等场景。而最常见的两种动态代理方式就是 JDK 动态代理CGLIB 动态代理。本文将带你全面了解它们的原理、区别、性能、Spring 应用方式及适用场景,并通过示例代码深入理解两者的使用方式。

目录

一、动态代理简介

二、JDK 动态代理原理

三、CGLIB 动态代理原理

四、核心区别

五、Spring 中的选择策略

六、示例代码对比

JDK 动态代理

CGLIB 动态代理

七、总结与建议

附加说明


一、动态代理简介

动态代理(Dynamic Proxy)是一种在运行时创建代理类并将调用转发给目标对象的机制。通过它,我们可以在不修改原始代码的情况下,增强方法逻辑,如日志记录、安全检查、事务处理等。

二、JDK 动态代理原理

JDK 动态代理依赖于 反射机制,要求目标类必须实现一个或多个接口。Java 在运行时生成一个实现了这些接口的代理类,调用被转发到 InvocationHandlerinvoke 方法中。

public interface UserService {
    void save();
}

public class UserServiceImpl implements UserService {
    public void save() {
        System.out.println("保存用户");
    }
}

// 创建代理
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    (proxy1, method, args) -> {
        System.out.println("前置逻辑");
        Object result = method.invoke(target, args);
        System.out.println("后置逻辑");
        return result;
    });

三、CGLIB 动态代理原理

CGLIB 采用底层的 ASM 字节码生成技术,为目标类生成一个子类,通过方法拦截器增强方法逻辑。不依赖接口,可直接代理普通类。

public class UserService {
    public void save() {
        System.out.println("保存用户");
    }
}

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
    System.out.println("前置逻辑");
    Object result = proxy.invokeSuper(obj, args);
    System.out.println("后置逻辑");
    return result;
});
UserService proxy = (UserService) enhancer.create();

四、核心区别

对比维度JDK 动态代理CGLIB 动态代理
原理反射 + 接口实现字节码生成子类
目标对象必须实现接口不需要接口
代理对象实现接口的类继承目标类的子类
限制无法代理类无法代理 final 类和 final 方法
性能(JDK8+)调用速度快,初始化较快初始化稍慢,调用速度相当
是否依赖第三方库否(Java 标准库)是(cglib + asm)

五、Spring 中的选择策略

在 Spring AOP 或事务管理中,代理选择的规则如下:

场景Spring 默认代理方式
类实现了接口使用 JDK 动态代理
类未实现接口使用 CGLIB
强制使用 CGLIB设置 @EnableAspectJAutoProxy(proxyTargetClass = true)

实战建议:若希望统一使用 CGLIB(如为了增强 private 方法),可以手动配置开启 CGLIB 模式。

六、示例代码对比

JDK 动态代理

public interface HelloService {
    void sayHello();
}

public class HelloServiceImpl implements HelloService {
    public void sayHello() {
        System.out.println("Hello");
    }
}
HelloService proxy = (HelloService) Proxy.newProxyInstance(
    HelloServiceImpl.class.getClassLoader(),
    new Class[]{HelloService.class},
    (proxy1, method, args) -> {
        System.out.println("Before");
        Object result = method.invoke(new HelloServiceImpl(), args);
        System.out.println("After");
        return result;
    });
proxy.sayHello();

CGLIB 动态代理

public class HelloService {
    public void sayHello() {
        System.out.println("Hello");
    }
}
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
    System.out.println("Before");
    Object result = proxy.invokeSuper(obj, args);
    System.out.println("After");
    return result;
});
HelloService proxy = (HelloService) enhancer.create();
proxy.sayHello();

七、总结与建议

  • ✅ 如果你的类 实现了接口,优先使用 JDK 动态代理,性能好,依赖少;

  • ✅ 如果类 没有接口,只能使用 CGLIB,但注意类和方法不能是 final

  • ✅ 在 Spring 中,两者可共存,Spring 会根据情况自动选择;

  • ✅ CGLIB 使用场景更多样,但也更复杂,建议按需使用。

附加说明

  • 若需要代理 private 方法,JDK 和 CGLIB 都无法直接实现,需使用更底层的 ASM 或 ByteBuddy;

  • 生产环境中推荐使用 Spring AOP 封装的方式,屏蔽代理底层细节。

如果你对这两种代理方式在 Spring AOP、事务管理、权限控制中的应用感兴趣,欢迎关注后续专栏文章 👇
下一篇:Spring AOP 源码剖析:代理模式选择机制揭秘

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值