SpringBoot项目中JDK动态代理和CGLIB动态代理的使用详解

 更新时间:2025年03月11日 08:57:24   作者:冰糖心书房  
JDK动态代理和CGLIB动态代理都是SpringBoot中实现AOP的重要技术,JDK动态代理通过反射生成代理类,适用于目标类实现了接口的场景,性能较好,易用性高,但必须实现接口且不能代理final方法,CGLIB动态代理通过生成子类实现代理

在 Spring Boot 项目中,JDK 动态代理和 CGLIB 动态代理都是实现 AOP (面向切面编程) 的重要技术。 它们的主要区别在于代理对象的生成方式和适用范围。

下面详细介绍它们的使用场景:

1. JDK 动态代理 (JDK Dynamic Proxy)

原理:

  • JDK 动态代理是 Java 提供的内置代理机制,它通过反射在运行时动态地生成代理类。
  • 代理类会实现目标类实现的接口,并将接口中的方法调用委托给一个 InvocationHandler 对象来处理。
  • InvocationHandler 接口负责实现具体的增强逻辑,例如日志记录、安全检查、事务管理等。

使用场景:

  • 目标类实现了接口: 如果目标类实现了接口,则 Spring AOP 默认使用 JDK 动态代理。
  • 简单 AOP 场景: 适用于简单的 AOP 场景,例如只需要对接口方法进行增强的情况。
  • 不需要代理类的构造函数: JDK 动态代理创建代理对象时,不需要调用目标类的构造函数。

优点:

  • 简单易用: JDK 动态代理是 Java 内置的代理机制,使用起来比较简单。
  • 不需要第三方库: 不需要依赖第三方库。
  • 对接口友好: 代理类实现了目标类实现的接口,符合面向接口编程的思想。

缺点:

  • 必须实现接口: 目标类必须实现接口,否则无法使用 JDK 动态代理。
  • 只能代理接口方法: 只能代理接口中定义的方法,无法代理类自身定义的方法。

示例代码:

// 接口
interface MyInterface {
    void doSomething();
}

// 实现类
class MyClass implements MyInterface {
    @Override
    public void doSomething() {
        System.out.println("MyClass is doing something...");
    }
}

// 调用处理器
class MyInvocationHandler implements InvocationHandler {
    private Object target; // 被代理的对象

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method: " + method.getName()); // 前置增强
        Object result = method.invoke(target, args); // 调用原始方法
        System.out.println("After method: " + method.getName()); // 后置增强
        return result;
    }
}

// 使用 JDK 动态代理
public class JDKDynamicProxyExample {
    public static void main(String[] args) {
        MyInterface target = new MyClass(); // 创建目标对象
        MyInvocationHandler handler = new MyInvocationHandler(target); // 创建调用处理器

        // 创建代理对象
        MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                MyInterface.class.getClassLoader(),
                new Class[] {MyInterface.class},
                handler
        );

        proxy.doSomething(); // 调用代理对象的方法,会被拦截
    }
}

2. CGLIB 动态代理 (CGLIB Dynamic Proxy)

原理:

  • CGLIB (Code Generation Library) 是一个强大的、高性能的代码生成库。
  • CGLIB 动态代理通过在运行时动态地生成目标类的子类来实现代理。
  • 代理类会继承目标类,并重写目标类的方法,在重写的方法中实现增强逻辑。
  • CGLIB 动态代理不需要目标类实现接口,可以直接代理类。

使用场景:

  • 目标类没有实现接口: 如果目标类没有实现接口,则 Spring AOP 使用 CGLIB 动态代理。
  • 需要代理类自身定义的方法: 需要代理类自身定义的方法,而不仅仅是接口方法。
  • 需要更高的性能: 在某些情况下,CGLIB 动态代理的性能可能比 JDK 动态代理更好。

优点:

  • 不需要实现接口: 目标类不需要实现接口,可以直接代理类。
  • 可以代理类自身定义的方法: 可以代理类自身定义的方法,而不仅仅是接口方法。
  • 性能较好: 在某些情况下,CGLIB 动态代理的性能可能比 JDK 动态代理更好。

缺点:

  • 需要第三方库: 需要依赖 CGLIB 库。
  • 实现复杂: CGLIB 动态代理的实现比较复杂,需要生成目标类的子类。
  • final 方法无法代理: 无法代理被 final 修饰的方法。
  • 对构造函数有要求: CGLIB 创建代理对象时,需要调用目标类的构造函数。 如果目标类没有无参构造函数,则需要手动指定构造函数。

示例代码:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 目标类
class MyClass {
    public void doSomething() {
        System.out.println("MyClass is doing something...");
    }
}

// 方法拦截器
class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method: " + method.getName()); // 前置增强
        Object result = proxy.invokeSuper(obj, args); // 调用原始方法
        System.out.println("After method: " + method.getName()); // 后置增强
        return result;
    }
}

// 使用 CGLIB 动态代理
public class CGLIBDynamicProxyExample {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer(); // 创建 Enhancer 对象
        enhancer.setSuperclass(MyClass.class); // 设置超类
        enhancer.setCallback(new MyMethodInterceptor()); // 设置回调

        MyClass proxy = (MyClass) enhancer.create(); // 创建代理对象

        proxy.doSomething(); // 调用代理对象的方法,会被拦截
    }
}

3. Spring Boot 中的 AOP 配置

在 Spring Boot 项目中,可以通过以下方式配置 AOP:

添加 AOP 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

编写切面类:

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.demo.service.*.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("Before executing method: " + joinPoint.getSignature());
    }
}

配置代理方式:

  • 默认情况下,Spring AOP 会根据目标类是否实现了接口来选择使用 JDK 动态代理或 CGLIB 动态代理。
  • 可以通过 @EnableAspectJAutoProxy 注解的 proxyTargetClass 属性来强制使用 CGLIB 动态代理。
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用 CGLIB 动态代理
public class AopConfig {
    // ...
}

总结:

特性JDK 动态代理CGLIB 动态代理
目标类要求必须实现接口不需要实现接口
代理对象生成方式实现接口继承类
性能一般较好
易用性简单复杂
是否需要第三方库是 (net.sf.cglib)
适用场景目标类实现了接口,简单 AOP 场景目标类没有实现接口,需要代理类自身定义的方法,性能要求较高
@EnableAspectJAutoProxy默认值:falseproxyTargetClass = true

在实际开发中,Spring AOP 会自动选择合适的代理方式。 如果没有特殊需求,可以使用默认配置。

如果需要强制使用 CGLIB 动态代理,可以设置

@EnableAspectJAutoProxy(proxyTargetClass = true)

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringBoot如何实现并发任务并返回结果

    SpringBoot如何实现并发任务并返回结果

    这篇文章主要介绍了SpringBoot如何实现并发任务并返回结果问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • 若依框架升级springBoot3启动druid-spring-boot-starter报错问题及解决

    若依框架升级springBoot3启动druid-spring-boot-starter报错问题及解决

    文章主要描述了在使用Spring Boot 3时,由于缺少`com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties`类型的bean而报错,解决方案是考虑在配置中定义该类型bean
    2026-01-01
  • 微信商家转账常见的一些问题汇总(附Java调用示例)

    微信商家转账常见的一些问题汇总(附Java调用示例)

    这篇文章主要介绍了微信商家转账常见的一些问题汇总,文中附Java调用示例,包括IP地址不允许调用接口、APPID不存在、未获取转账场景、暂不支持展示用户收款感知、未传入完整且对应的转账场景报备信息以及商户运营账户资金不足等问题的解决步骤,需要的朋友可以参考下
    2025-12-12
  • 完美解决单例设计模式中懒汉式线程安全的问题

    完美解决单例设计模式中懒汉式线程安全的问题

    下面小编就为大家带来一篇完美解决单例设计模式中懒汉式线程安全的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • Java多线程中常见的锁策略详解

    Java多线程中常见的锁策略详解

    这篇文章主要介绍了Java多线程中常见的锁策略详解,在Java多线程中锁(synchronized)也会根据锁的竞争程度来升级为相关“高等级”锁,本文为了更好的理解 synchronized 加锁机制,对其做出了详细解释,需要的朋友可以参考下
    2023-07-07
  • Java8通过CompletableFuture实现异步回调

    Java8通过CompletableFuture实现异步回调

    这篇文章主要介绍了Java8通过CompletableFuture实现异步回调,CompletableFuture是Java 8 中新增的一个类,它是对Future接口的扩展,下文关于其更多相关详细介绍需要的小伙伴可以参考一下
    2022-04-04
  • Maven插件的安装及使用

    Maven插件的安装及使用

    这篇文章主要介绍了Maven插件的安装及使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • Java在控制台输出带颜色字符的2种方式详解

    Java在控制台输出带颜色字符的2种方式详解

    这篇文章主要给大家介绍了关于Java在控制台输出带颜色字符的2种方式,文中通过实例代码以及图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2022-01-01
  • 基于Java的界面开发详细步骤(用户注册登录)

    基于Java的界面开发详细步骤(用户注册登录)

    通过一段时间Java Web的学习,写一个简单的注册登陆界面来做个总结,这篇文章主要给大家介绍了基于Java的界面开发(用户注册登录)的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-01-01
  • Java基础学习之关键字和变量数据类型的那些事

    Java基础学习之关键字和变量数据类型的那些事

    变量就是系统为程序分配的一块内存单元,用来存储各种类型的数据,下面这篇文章主要给大家介绍了关于Java基础学习之关键字和变量数据类型的那些事,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07

最新评论