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)

总结

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

相关文章

  • spring boot如何加入mail邮件支持

    spring boot如何加入mail邮件支持

    这篇文章主要介绍了spring boot如何加入mail邮件支持,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • java编程调用存储过程中得到新增记录id号的实现方法

    java编程调用存储过程中得到新增记录id号的实现方法

    这篇文章主要介绍了java编程调用存储过程中得到新增记录id号的实现方法,涉及Java数据库操作中存储过程的相关使用技巧,需要的朋友可以参考下
    2015-10-10
  • Jenkins Pipeline为Kubernetes应用部署增加状态检测脚本优化

    Jenkins Pipeline为Kubernetes应用部署增加状态检测脚本优化

    这篇文章主要为大家介绍了Jenkins Pipeline为Kubernetes应用部署增加状态检测脚本优化示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • SpringBoot获取http数据、打印HTTP参数的4种方式

    SpringBoot获取http数据、打印HTTP参数的4种方式

    Java的话本地打断点可以调试获取rest入参,但是在生产环境可能我们获取入参(Http header/parameter)可能就没有那么的轻松了,所以本文给大家介绍了SpringBoot获取http数据、打印HTTP参数的4种方式,需要的朋友可以参考下
    2024-03-03
  • springboot如何通过不同的策略动态调用不同的实现类

    springboot如何通过不同的策略动态调用不同的实现类

    这篇文章主要介绍了springboot如何通过不同的策略动态调用不同的实现类,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • SpringCloud-Alibaba-Sentinel-配置持久化策略详解

    SpringCloud-Alibaba-Sentinel-配置持久化策略详解

    这篇文章主要介绍了SpringCloud-Alibaba-Sentinel-配置持久化策略,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • 新手学习java反射基础

    新手学习java反射基础

    Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。下面我们来一起学习一下吧
    2019-06-06
  • 零基础如何系统的学习Java

    零基础如何系统的学习Java

    这篇文章主要介绍了零基础如何系统的学习Java,很多朋友纠结这个问题,教材书不知道从何学起,今天小编给大家分享一篇教程帮助到家梳理这方面的知识
    2020-07-07
  • java实现肯德基收银系统

    java实现肯德基收银系统

    这篇文章主要为大家详细介绍了java实现肯德基收银系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • mybatis中#{}和${}的区别详解

    mybatis中#{}和${}的区别详解

    本文主要介绍了mybatis中#{}和${}的区别详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01

最新评论