设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间12 现在时间12 缓存数据 做人啊,最要紧的是开心 你饿不饿,我去给你煮碗面 其实我不想给你煮面我只是想见你一面

做人啊,最要紧的是开心 你饿不饿,我去给你煮碗面 其实我不想给你煮面我只是想见你一面 -- 问你是否跟我走

查看: 498|回复: 0

SpringBoot中5种动态代理的实现方案

[复制链接]

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
30
主题
28
精华
0
金钱
96
积分
58
注册时间
2023-10-4
最后登录
2025-5-31

发表于 2025-5-31 05:57:01 | 显示全部楼层 |阅读模式

动态代理允许我们在不修改源代码的情况下,为对象增加额外的行为。在SpringBoot应用中,动态代理被广泛用于实现事务管理、缓存、安全控制、日志记录等横切关注点。

1. JDK动态代理:Java原生的代理方案

实现原理

JDK动态代理是Java标准库提供的代理机制,基于

  1. java.lang.reflect.Proxy
复制代码
类和
  1. InvocationHandler
复制代码
接口实现。它通过反射在运行时动态创建接口的代理实例。

核心代码示例

  1. public class JdkDynamicProxyDemo {
  2. interface UserService {
  3. void save(User user);
  4. User find(Long id);
  5. }
  6. // 实现类
  7. static class UserServiceImpl implements UserService {
  8. @Override
  9. public void save(User user) {
  10. System.out.println("保存用户: " + user.getName());
  11. }
  12. @Override
  13. public User find(Long id) {
  14. System.out.println("查找用户ID: " + id);
  15. return new User(id, "用户" + id);
  16. }
  17. }
  18. // 调用处理器
  19. static class LoggingInvocationHandler implements InvocationHandler {
  20. private final Object target;
  21. public LoggingInvocationHandler(Object target) {
  22. this.target = target;
  23. }
  24. @Override
  25. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  26. System.out.println("Before: " + method.getName());
  27. long startTime = System.currentTimeMillis();
  28. Object result = method.invoke(target, args);
  29. long endTime = System.currentTimeMillis();
  30. System.out.println("After: " + method.getName() + ", 耗时: " + (endTime - startTime) + "ms");
  31. return result;
  32. }
  33. }
  34. public static void main(String[] args) {
  35. // 创建目标对象
  36. UserService userService = new UserServiceImpl();
  37. // 创建代理对象
  38. UserService proxy = (UserService) Proxy.newProxyInstance(
  39. userService.getClass().getClassLoader(),
  40. userService.getClass().getInterfaces(),
  41. new LoggingInvocationHandler(userService)
  42. );
  43. // 调用代理方法
  44. proxy.save(new User(1L, "张三"));
  45. User user = proxy.find(2L);
  46. }
  47. }
复制代码

优点

  • JDK标准库自带:无需引入额外依赖,减少了项目体积
  • 生成代码简单:代理逻辑集中在InvocationHandler中,易于理解和维护
  • 性能相对稳定:在JDK 8后的版本中,性能有明显提升

局限性

  • 只能代理接口:被代理类必须实现接口,无法代理类
  • 反射调用开销:每次方法调用都需通过反射机制,有一定性能损耗
  • 无法拦截final方法:无法代理被final修饰的方法

Spring中的应用

在Spring中,当Bean实现了接口时,默认使用JDK动态代理。这可以通过配置修改:

  1. @EnableAspectJAutoProxy(proxyTargetClass = false) // 默认值为false,表示优先使用JDK动态代理
复制代码

2. CGLIB代理:基于字节码的强大代理

实现原理

CGLIB(Code Generation Library)是一个强大的高性能字节码生成库,它通过继承被代理类生成子类的方式实现代理。Spring从3.2版本开始将CGLIB直接集成到框架中。

核心代码示例

  1. public class CglibProxyDemo {
  2. // 不需要实现接口的类
  3. static class UserService {
  4. public void save(User user) {
  5. System.out.println("保存用户: " + user.getName());
  6. }
  7. public User find(Long id) {
  8. System.out.println("查找用户ID: " + id);
  9. return new User(id, "用户" + id);
  10. }
  11. }
  12. // CGLIB方法拦截器
  13. static class LoggingMethodInterceptor implements MethodInterceptor {
  14. @Override
  15. public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
  16. System.out.println("Before: " + method.getName());
  17. long startTime = System.currentTimeMillis();
  18. // 调用原始方法
  19. Object result = proxy.invokeSuper(obj, args);
  20. long endTime = System.currentTimeMillis();
  21. System.out.println("After: " + method.getName() + ", 耗时: " + (endTime - startTime) + "ms");
  22. return result;
  23. }
  24. }
  25. public static void main(String[] args) {
  26. // 创建CGLIB代理
  27. Enhancer enhancer = new Enhancer();
  28. enhancer.setSuperclass(UserService.class);
  29. enhancer.setCallback(new LoggingMethodInterceptor());
  30. // 创建代理对象
  31. UserService proxy = (UserService) enhancer.create();
  32. // 调用代理方法
  33. proxy.save(new User(1L, "张三"));
  34. User user = proxy.find(2L);
  35. }
  36. }
复制代码

优点

  • 可以代理类:不要求目标类实现接口,应用场景更广泛
  • 性能较高:通过生成字节码而非反射调用,方法调用性能优于JDK代理
  • 功能丰富:支持多种回调类型,如LazyLoader、Dispatcher等
  • 集成到Spring:Spring框架已内置CGLIB,无需额外依赖

局限性

  • 无法代理final类和方法:由于使用继承机制,无法代理final修饰的类或方法
  • 构造函数调用:在生成代理对象时会调用目标类的构造函数,可能导致意外行为
  • 复杂性增加:生成的字节码复杂度高,调试困难
  • 对Java版本敏感:在不同Java版本间可能存在兼容性问题

Spring中的应用

在Spring中,当Bean没有实现接口或配置了

  1. proxyTargetClass=true
复制代码
时,使用CGLIB代理:

  1. @EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB代理
复制代码

3. ByteBuddy:现代化的字节码操作库

实现原理

ByteBuddy是一个相对较新的字节码生成和操作库,设计更加现代化,API更加友好。它可以创建和修改Java类,而无需理解底层的JVM指令集。

核心代码示例

  1. public class ByteBuddyProxyDemo {
  2. interface UserService {
  3. void save(User user);
  4. User find(Long id);
  5. }
  6. static class UserServiceImpl implements UserService {
  7. @Override
  8. public void save(User user) {
  9. System.out.println("保存用户: " + user.getName());
  10. }
  11. @Override
  12. public User find(Long id) {
  13. System.out.println("查找用户ID: " + id);
  14. return new User(id, "用户" + id);
  15. }
  16. }
  17. static class LoggingInterceptor {
  18. @RuntimeType
  19. public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable,
  20. @AllArguments Object[] args) throws Exception {
  21. System.out.println("Before: " + method.getName());
  22. long startTime = System.currentTimeMillis();
  23. Object result = callable.call();
  24. long endTime = System.currentTimeMillis();
  25. System.out.println("After: " + method.getName() + ", 耗时: " + (endTime - startTime) + "ms");
  26. return result;
  27. }
  28. }
  29. public static void main(String[] args) throws Exception {
  30. UserService userService = new UserServiceImpl();
  31. // 创建ByteBuddy代理
  32. UserService proxy = new ByteBuddy()
  33. .subclass(UserServiceImpl.class)
  34. .method(ElementMatchers.any())
  35. .intercept(MethodDelegation.to(new LoggingInterceptor()))
  36. .make()
  37. .load(UserServiceImpl.class.getClassLoader())
  38. .getLoaded()
  39. .getDeclaredConstructor()
  40. .newInstance();
  41. // 调用代理方法
  42. proxy.save(new User(1L, "张三"));
  43. User user = proxy.find(2L);
  44. }
  45. }
复制代码

优点

  • 流畅的API:提供链式编程风格,代码更加可读
  • 性能卓越:在多项基准测试中,性能优于CGLIB和JDK代理
  • 类型安全:API设计更注重类型安全,减少运行时错误
  • 支持Java新特性:对Java 9+模块系统等新特性有更好的支持
  • 功能丰富:支持方法重定向、字段访问、构造函数拦截等多种场景

局限性

  • 额外依赖:需要引入额外的依赖库
  • 学习曲线:API虽然流畅,但概念较多,有一定学习成本
  • 在Spring中集成度不高:需要自定义配置才能在Spring中替代默认代理机制

Spring中的应用

ByteBuddy虽然不是Spring默认的代理实现,但可以通过自定义

  1. ProxyFactory
复制代码
来集成:

  1. @Configuration
  2. public class ByteBuddyProxyConfig {
  3. @Bean
  4. public AopConfigurer byteBuddyAopConfigurer() {
  5. return new AopConfigurer() {
  6. @Override
  7. public void configureProxyCreator(Object bean, String beanName) {
  8. // 配置ByteBuddy作为代理创建器
  9. // 实现细节略
  10. }
  11. };
  12. }
  13. }
复制代码

4. Javassist:更易用的字节码编辑库

实现原理

Javassist是一个开源的Java字节码操作库,提供了两个层次的API:源代码级别和字节码级别。它允许开发者用简单的Java语法直接编辑字节码,无需深入了解JVM规范。

核心代码示例

  1. public class JavassistProxyDemo {
  2. interface UserService {
  3. void save(User user);
  4. User find(Long id);
  5. }
  6. static class UserServiceImpl implements UserService {
  7. @Override
  8. public void save(User user) {
  9. System.out.println("保存用户: " + user.getName());
  10. }
  11. @Override
  12. public User find(Long id) {
  13. System.out.println("查找用户ID: " + id);
  14. return new User(id, "用户" + id);
  15. }
  16. }
  17. public static void main(String[] args) throws Exception {
  18. ProxyFactory factory = new ProxyFactory();
  19. // 设置代理接口
  20. factory.setInterfaces(new Class[] { UserService.class });
  21. // 创建方法过滤器
  22. MethodHandler handler = new MethodHandler() {
  23. private UserService target = new UserServiceImpl();
  24. @Override
  25. public Object invoke(Object self, Method method, Method proceed, Object[] args) throws Throwable {
  26. System.out.println("Before: " + method.getName());
  27. long startTime = System.currentTimeMillis();
  28. Object result = method.invoke(target, args);
  29. long endTime = System.currentTimeMillis();
  30. System.out.println("After: " + method.getName() + ", 耗时: " + (endTime - startTime) + "ms");
  31. return result;
  32. }
  33. };
  34. // 创建代理对象
  35. UserService proxy = (UserService) factory.create(new Class<?>[0], new Object[0], handler);
  36. // 调用代理方法
  37. proxy.save(new User(1L, "张三"));
  38. User user = proxy.find(2L);
  39. }
  40. }
复制代码

优点

  • 源代码级API:可以不懂字节码指令集,以Java代码形式操作字节码
  • 轻量级库:相比其他字节码库体积较小
  • 功能全面:支持创建类、修改类、动态编译Java源代码等
  • 性能良好:生成的代理代码性能接近CGLIB
  • 长期维护:库历史悠久,稳定可靠

局限性

  • API不够直观:部分API设计较为古老,使用不如ByteBuddy流畅
  • 文档不足:相比其他库,文档和示例较少
  • 内存消耗:在操作大量类时可能消耗较多内存

Spring中的应用

Javassist不是Spring默认的代理实现,但一些基于Spring的框架使用它实现动态代理,如Hibernate:

  1. // 自定义Javassist代理工厂示例
  2. public class JavassistProxyFactory implements ProxyFactory {
  3. @Override
  4. public Object createProxy(Object target, Interceptor interceptor) {
  5. // Javassist代理实现
  6. // ...
  7. }
  8. }
复制代码

5. AspectJ:完整的AOP解决方案

实现原理

AspectJ是一个完整的AOP框架,与前面的动态代理方案不同,它提供两种方式:

  • 编译时织入:在编译源代码时直接修改字节码
  • 加载时织入:在类加载到JVM时修改字节码

AspectJ拥有专门的切面语言,功能比Spring AOP更强大。

核心代码示例

AspectJ切面定义:

  1. @Aspect
  2. public class LoggingAspect {
  3. @Before("execution(* com.example.service.UserService.*(..))")
  4. public void logBefore(JoinPoint joinPoint) {
  5. System.out.println("Before: " + joinPoint.getSignature().getName());
  6. }
  7. @Around("execution(* com.example.service.UserService.*(..))")
  8. public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
  9. System.out.println("Before: " + joinPoint.getSignature().getName());
  10. long startTime = System.currentTimeMillis();
  11. Object result = joinPoint.proceed();
  12. long endTime = System.currentTimeMillis();
  13. System.out.println("After: " + joinPoint.getSignature().getName() +
  14. ", 耗时: " + (endTime - startTime) + "ms");
  15. return result;
  16. }
  17. }
复制代码

Spring配置AspectJ:

  1. @Configuration
  2. @EnableAspectJAutoProxy
  3. public class AspectJConfig {
  4. @Bean
  5. public LoggingAspect loggingAspect() {
  6. return new LoggingAspect();
  7. }
  8. }
复制代码

编译时织入配置(maven):

  1. <plugin>
  2. <groupId>org.codehaus.mojo</groupId>
  3. <artifactId>aspectj-maven-plugin</artifactId>
  4. <version>1.14.0</version>
  5. <configuration>
  6. <complianceLevel>11</complianceLevel>
  7. <source>11</source>
  8. <target>11</target>
  9. <aspectLibraries>
  10. <aspectLibrary>
  11. <groupId>org.springframework</groupId>
  12. <artifactId>spring-aspects</artifactId>
  13. </aspectLibrary>
  14. </aspectLibraries>
  15. </configuration>
  16. <executions>
  17. <execution>
  18. <goals>
  19. <goal>compile</goal>
  20. </goals>
  21. </execution>
  22. </executions>
  23. </plugin>
复制代码

优点

  • 功能最全面:支持几乎所有AOP场景,包括构造函数、字段访问、异常等
  • 性能最优:编译时织入无运行时开销,性能接近原生代码
  • 完整语言支持:有专门的切面语言和语法,表达能力强
  • 更灵活的切入点:可以对接口、实现类、构造函数、字段等定义切面

局限性

  • 复杂度高:学习曲线陡峭,需要掌握AspectJ语法
  • 构建过程改变:需要特殊的编译器或类加载器
  • 调试难度增加:修改后的字节码可能难以调试

Spring中的应用

Spring可以通过以下方式使用AspectJ:

proxy-target-class模式:使用Spring AOP但CGLIB生成的代理

  1. @EnableAspectJAutoProxy(proxyTargetClass = true)
复制代码

LTW(Load-Time Weaving)模式:在类加载时织入

  1. @EnableLoadTimeWeaving
复制代码

编译时织入:需要配置AspectJ编译器

使用场景对比与选择建议

代理实现最适用场景不适用场景
JDK动态代理基于接口的简单代理,轻量级应用没有实现接口的类,性能敏感场景
CGLIB没有实现接口的类,需要兼顾性能和便捷性final类/方法,高安全性环境
ByteBuddy现代化项目,关注性能优化,复杂代理逻辑追求最小依赖的简单项目
Javassist需要动态生成/修改类的复杂场景API设计敏感项目,初学者
AspectJ企业级应用,性能关键型场景,复杂AOP需求简单项目,快速原型,学习成本敏感

到此这篇关于SpringBoot中5种动态代理的实现方案的文章就介绍到这了,更多相关SpringBoot动态代理内容请搜索晓枫资讯以前的文章或继续浏览下面的相关文章希望大家以后多多支持晓枫资讯!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表