当前位置: 首页 > news >正文

Spring Boot 项目中,JDK 动态代理和 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)

相关文章:

  • python中httpx库的详细使用及案例
  • 开关电源布局要点
  • 【数据结构】(Python)第六章:图
  • 【游戏——BFS+分层图】
  • 剖析IO原理和零拷贝机制
  • 【实战中提升自己】防火墙篇之双ISP切换与VRRP切换对于用户的体验
  • 【Java项目】基于Spring Boot的简历系统
  • 以 Tomcat 为例分析 Java 中的线程池
  • 正态分布的奇妙性质:为什么奇数阶中心矩(odd central moments)为零?
  • LeetCode 230.二叉搜索树中第K小的元素
  • MySQL自动备份脚本
  • 游戏引擎学习第120天
  • LeetCode刷题 -- 23. 合并 K 个升序链表
  • OO设计原则的cpp举例
  • centos 7 安装python3 及pycharm远程连接方法
  • 【信息系统项目管理师-案例真题】2009上半年案例分析答案和详解
  • 心理咨询小程序的未来发展
  • Java 面试合集(2024版)
  • 力扣3102.最小化曼哈顿距离
  • linux--多进程基础(2)GDB多进程调试(面试会问)
  • 企业网站的web应用环境通常有几种组合/mac923水蜜桃923色号
  • 西安疫情最新数据消息今天/杭州seo联盟
  • 免费做相册video的网站/seo人才网
  • 苏州响应式网站建设/2023最火的十大新闻
  • 邢台做网站优化价格/中国知名网站排行榜
  • 手机网站用什么软件做的好/广州seo公司排行