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

Spring | JDK 动态代理与 CGLIB 代理:原理、区别与实战对比

CodingTechWork

引言

  在 Java 开发中,动态代理技术被广泛应用于 AOP(面向切面编程)等场景。JDK 动态代理和 CGLIB 代理是两种常见的动态代理实现方式。它们各有特点和适用场景。本文将详细介绍这两种代理技术的原理、区别,并通过示例代码展示它们的实际应用,帮助你更好地选择合适的代理方式。

动态代理的基本概念

  动态代理是指在运行时动态生成代理类和代理对象的技术。代理对象可以拦截对目标对象的调用,并在调用前后执行额外的逻辑。动态代理的核心在于:

  • 代理类:在运行时动态生成的类,实现了与目标类相同的接口或继承了目标类。
  • 代理对象:通过代理类创建的对象,用于拦截对目标对象的调用。
  • 目标对象:实际执行业务逻辑的对象。

JDK 动态代理

基本原理

  JDK 动态代理是基于 Java 的反射机制实现的。它要求目标类必须实现一个接口。代理类在运行时动态生成,实现了与目标类相同的接口。通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来创建代理对象。

示例代码

定义接口

public interface MyService {void doSomething();
}

实现接口的目标类

public class MyServiceImpl implements MyService {@Overridepublic void doSomething() {System.out.println("Doing something...");}
}

实现 InvocationHandler

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class MyInvocationHandler implements InvocationHandler {private final Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic 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;}
}

测试代码

import java.lang.reflect.Proxy;public class JdkProxyExample {public static void main(String[] args) {MyService target = new MyServiceImpl();MyInvocationHandler handler = new MyInvocationHandler(target);MyService proxy = (MyService) Proxy.newProxyInstance(MyService.class.getClassLoader(),new Class<?>[]{MyService.class},handler);proxy.doSomething();}
}

输出结果

Before method: doSomething
Doing something...
After method: doSomething

优点

  • 简单易用:基于 Java 的反射机制,使用起来非常简单。
  • 性能较好:在某些场景下,性能表现较好,尤其是当代理类的方法调用较少时。

缺点

  • 必须实现接口:目标类必须实现一个接口,否则无法使用 JDK 动态代理。
  • 灵活性有限:由于依赖接口,无法代理目标类的非接口方法。

CGLIB 代理

基本原理

  CGLIB(Code Generation Library)是一个高性能的代码生成库,它通过字节码操作技术动态生成目标类的子类。因此,CGLIB 代理不需要目标类实现接口,但目标类不能是 final 类。通过 net.sf.cglib.proxy.Enhancer 类来创建代理对象。代理类在运行时动态生成,继承了目标类,并通过 MethodInterceptor 接口来处理方法调用。

示例代码

目标类

public class MyService {public void doSomething() {System.out.println("Doing something...");}
}

实现 MethodInterceptor

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class MyMethodInterceptor implements MethodInterceptor {@Overridepublic 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;}
}

测试代码

import net.sf.cglib.proxy.Enhancer;public class CglibProxyExample {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(MyService.class);enhancer.setCallback(new MyMethodInterceptor());MyService proxy = (MyService) enhancer.create();proxy.doSomething();}
}

输出结果

Before method: doSomething
Doing something...
After method: doSomething

优点

  • 无需接口:不需要目标类实现接口,可以代理任意类。
  • 高性能:在某些场景下,性能表现优于 JDK 动态代理,尤其是在代理类的方法调用较多时。

缺点

  • 复杂度较高:基于字节码操作技术,实现相对复杂。
  • 不能代理final类:目标类不能是 final 类,因为 CGLIB 通过生成子类来实现代理。

性能对比

初始化时间

  • JDK 动态代理:初始化时间相对较短,因为它是基于反射机制实现的。
  • CGLIB 代理:初始化时间相对较长,因为它需要在运行时动态生成字节码。

方法调用时间

  • JDK 动态代理:在方法调用较少时,性能表现较好。
  • CGLIB 代理:在方法调用较多时,性能表现更好,因为它生成的代理类是目标类的子类,方法调用的开销相对较小。

使用建议

  • 优先使用 JDK 动态代理:如果目标类实现了接口,优先使用 JDK 动态代理,因为它简单易用且性能较好。
  • 使用 CGLIB 代理:如果目标类没有实现接口,或者需要代理目标类的非接口方法,可以使用 CGLIB 代理。

总结

  JDK 动态代理和 CGLIB 代理各有优缺点,适用于不同的场景。JDK 动态代理简单易用,但需要目标类实现接口;CGLIB 代理无需接口,性能在某些场景下更好,但实现相对复杂。在实际开发中,可以根据具体需求选择合适的代理技术。

http://www.xdnf.cn/news/984223.html

相关文章:

  • Docker部署minio
  • AIStor 的模型上下文协议 (MCP) 服务器:管理功能
  • 什么是 Solana 上的 MEV?一键狙击是如何保护你的代币启动的?
  • ANeko v1.0.3 | 在手机里养只宠物猫 实时互动 动画细腻
  • 递归,回溯,DFS,Floodfill,记忆化搜索
  • 三.Gitee远程操作标签操作
  • Java使用Selenium反爬虫优化方案
  • 力反馈手套:工业虚拟现实培训领域的革新者
  • [蓝桥杯 2024 国 Python B] 设计
  • Spring Security如何拿到登录用户的信息
  • 安卓9.0系统修改定制化____系列讲解导读篇
  • 【C/C++】怎样设计一个合理的函数
  • 咖啡豆缺陷检测:用YOLOv8+TensorFlow实现工业级质检系统
  • 临时抱佛脚v2
  • 费用流学习笔记
  • C++内存池:减少动态分配开销的高效解决方案
  • R语言缓释制剂QBD解决方案之二
  • 如何使用vue2设计提示框组件
  • 解决华为云服务器无法ping通github问题
  • Java NIO 面试全解析:9大核心考点与深度剖析
  • Langfuse 深度使用指南:构建可观测的LLM应用系统
  • 蓝桥杯刷题
  • 腾讯位置商业授权危险地点查询开发指南
  • 【愚公系列】《生产线数字化设计与仿真》009-颜色分类站仿真(设置颜色分类站的仿真序列)
  • AI日报 - 2025年06月11日
  • ElasticSearch配置详解:什么是重平衡
  • 【MySQL 从 0 讲解系列】深入理解 GROUP BY 的本质与应用(含SQL示例+面试题)
  • 无刷直流电机控制系统仿真建模
  • 修仙处于平凡
  • 用Python撬动量化交易:深入探索开源利器vnpy