从@Async案例找到Spring框架的bug:exposeProxy=true不生效原因大剖析+最佳解决方案【享学Spring】(上)

简介: 从@Async案例找到Spring框架的bug:exposeProxy=true不生效原因大剖析+最佳解决方案【享学Spring】(上)

前言


本文标题包含有'靓丽'的字眼:Spring框架bug。相信有的小伙伴心里小九九就会说了:又是一篇标题党文章。

鉴于此,此处可以很负责任的对大伙说:本人所有文章绝不哗众取宠,除了干货只剩干货。


相信关注过我的小伙伴都是知道的,我只递送干货,绝不标题党来浪费大家的时间和精力~那无异于谋财害命(说得严重了,不喜勿喷)

关于标题党的好与坏、优与劣,此处我不置可否


本篇文章能让你知道exposeProxy=true真实作用和实际作用范围,从而能够在开发中更精准的使用到它。


背景


这篇文章可定位为是基于上篇文章的续文:

【小家Spring】使用@Async异步注解导致该Bean在循环依赖时启动报BeanCurrentlyInCreationException异常的根本原因分析,以及提供解决方案


本来一切本都那么平静,直到我用了@Async注解,好多问题都接踵而至(上篇文章已经解决大部分了)。在上篇文章中,为了解决@Async同类方法调用问题我提出了两个方向的解决方案:


  1. 自己注入自己,然后再调用接口方法(当然此处的一个变种是使用编程方式形如:AInterface a = applicationContext.getBean(AInterface.class);这样子手动获取也是可行的~~~本文不讨论这种比较直接简单的方式)
  2. 使用AopContext.currentProxy();方式


方案一上篇文章已花笔墨重点分析,毕竟方案一我认为更为重要些。本文分析使用方案二的方式,它涉及到AOP、代理对象的暴露,因此我认为本文的内容对你平时开发的影响是不容小觑,可以重点浏览咯~


我相信绝大多数小伙伴都遇到过这个异常:


 java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
  at org.springframework.aop.framework.AopContext.currentProxy(AopContext.java:69)
  at com.fsx.dependency.B.funTemp(B.java:14)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
  at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:206)
  at com.sun.proxy.$Proxy44.funTemp(Unknown Source)
  ...

然后当你去靠度娘搜索解决方案时,发现无一例外都教你只需要这么做就成:

@EnableAspectJAutoProxy(exposeProxy = true)


本文我想说的可能又是一个技术敏感性问题,其实绝大多数情况下你按照这么做是可行的,直到你遇到了@Async也需要调用本类方法的时候,你就有点绝望了,然后本文或许会成为了你的救星~


本以为加了exposeProxy = true就能顺风顺水了,但它却出问题了:依旧报如上的异常信息。如果你看到这里也觉得不可思议,那么本文就更能体现它的价值所在~


此问题我个人把它归类为Spring的bug我觉得是无可厚非的,因为它的语义与实际表现出来的结果想悖了,so我把定义为Spring框架的bug。

对使用者来说,标注了exposeProxy = true,理论上就应该能够通过AopContext.currentProxy()拿到代理对象,可惜Spring这里却掉链子了,有点名不副实之感~


示例

本文将以多个示例来模拟不同的使用case,首先从直观的结果上先了解@EnableAspectJAutoProxy(exposeProxy = true)的作用以及它存在的问题。


备注:下面所有示例都建立在@EnableAspectJAutoProxy(exposeProxy = true)已经开启的前提下,形如:

@Configuration
@EnableAspectJAutoProxy(exposeProxy = true) // 暴露当前代理对象到当前线程绑定
public class RootConfig {
}


示例一

此示例大都用于解决事务不生效问题上(同类方法调用引起的事务不生效,关于Spring事务不生效的case,可以参考:【小家java】Spring事务不生效的原因大解读 )。


@Service
public class B implements BInterface {
    @Transactional
    @Override
    public void funTemp() {
        ...
        // 希望调用本类方法  但是它抛出异常,希望也能够回滚事务
        BInterface b = BInterface.class.cast(AopContext.currentProxy());
        System.out.println(b);
        b.funB();
    }
    @Override
    public void funB() {
        // ... 处理业务属于  
        System.out.println(1 / 0);
    }
}


结论:能正常work,事务也会生效~


示例二

同类内方法调用,希望异步执行被调用的方法(希望@Async生效)

@Service
public class B implements BInterface {
    @Override
    public void funTemp() {
        System.out.println("线程名称:" + Thread.currentThread().getName());
        // 希望调用本类方法  但是希望它去异步执行~
        BInterface b = BInterface.class.cast(AopContext.currentProxy());
        System.out.println(b);
        b.funB();
    }
    @Async
    @Override
    public void funB() {
        System.out.println("线程名称:" + Thread.currentThread().getName());
    }
}


结论:执行即报错


java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.


示例三

同类内方法调用,希望异步执行被调用的方法,并且在入口方法处使用事务

@Service
public class B implements BInterface {
    @Transactional
    @Override
    public void funTemp() {
        System.out.println("线程名称:" + Thread.currentThread().getName());
        // 希望调用本类方法  但是希望它去异步执行~
        BInterface b = BInterface.class.cast(AopContext.currentProxy());
        System.out.println(b);
        b.funB();
    }
    @Async
    @Override
    public void funB() {
        System.out.println("线程名称:" + Thread.currentThread().getName());
    }
}

结论:正常work没有报错,@Async异步生效、事务也生效


示例四


示例三的唯一区别是把事务注解@Transactional标注在被调用的方法处(和@Async同方法):


@Service
public class B implements BInterface {
    @Override
    public void funTemp() {
        System.out.println("线程名称:" + Thread.currentThread().getName());
        // 希望调用本类方法  但是希望它去异步执行~
        BInterface b = BInterface.class.cast(AopContext.currentProxy());
        System.out.println(b);
        b.funB();
    }
    @Transactional
    @Async
    @Override
    public void funB() {
        System.out.println("线程名称:" + Thread.currentThread().getName());
    }
}


结论:同示例三


示例五


@Async标注在入口方法上:

@Service
public class B implements BInterface {
    @Transactional
    @Async
    @Override
    public void funTemp() {
        System.out.println("线程名称:" + Thread.currentThread().getName());
        BInterface b = BInterface.class.cast(AopContext.currentProxy());
        System.out.println(b);
        b.funB();
    }
    @Override
    public void funB() {
        System.out.println("线程名称:" + Thread.currentThread().getName());
    }
}


结论:请求即报错


java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
  at org.springframework.aop.framework.AopContext.currentProxy(AopContext.java:69)


相关文章
|
27天前
|
人工智能 监控 安全
智慧工地解决方案,Spring Cloud智慧工地源代码
智慧工地平台针对建筑工地人员管理难、机械设备繁多、用电安全及施工环境复杂等问题,通过集成应用和硬件设备,实现数据互联互通与集中展示。基于微服务架构(Java+Spring Cloud+UniApp+MySql),平台支持PC端、手机端、平板端、大屏端管理,涵盖人员实名制、工资考勤、视频AI监控、绿色施工、危大工程监测、物料管理和安全质量管理等功能,助力施工现场的数字化、智能化综合管理,提升效率与安全性。
55 15
|
18天前
|
SQL Java 数据库连接
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
107 29
|
7天前
|
网络协议 Java Shell
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
33 7
|
7天前
|
XML Java 开发者
通过springboot框架创建对象(一)
在Spring Boot中,对象创建依赖于Spring框架的核心特性——控制反转(IoC)和依赖注入(DI)。IoC将对象的创建和管理交由Spring应用上下文负责,开发者只需定义依赖关系。DI通过构造函数、setter方法或字段注入实现依赖对象的传递。Spring Boot的自动配置机制基于类路径和配置文件,自动为应用程序配置Spring容器,简化开发过程。Bean的生命周期包括定义扫描、实例化、依赖注入、初始化和销毁回调,均由Spring容器管理。这些特性提高了开发效率并简化了代码维护。
|
1月前
|
开发框架 运维 监控
Spring Boot中的日志框架选择
在Spring Boot开发中,日志管理至关重要。常见的日志框架有Logback、Log4j2、Java Util Logging和Slf4j。选择合适的日志框架需考虑性能、灵活性、社区支持及集成配置。本文以Logback为例,演示了如何记录不同级别的日志消息,并强调合理配置日志框架对提升系统可靠性和开发效率的重要性。
|
2月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
2月前
|
Java 开发者 Spring
理解和解决Spring框架中的事务自调用问题
事务自调用问题是由于 Spring AOP 代理机制引起的,当方法在同一个类内部自调用时,事务注解将失效。通过使用代理对象调用、将事务逻辑分离到不同类中或使用 AspectJ 模式,可以有效解决这一问题。理解和解决这一问题,对于保证 Spring 应用中的事务管理正确性至关重要。掌握这些技巧,可以提高开发效率和代码的健壮性。
128 13
|
2月前
|
运维 监控 Java
为何内存不够用?微服务改造启动多个Spring Boot的陷阱与解决方案
本文记录并复盘了生产环境中Spring Boot应用内存占用过高的问题及解决过程。系统上线初期运行正常,但随着业务量上升,多个Spring Boot应用共占用了64G内存中的大部分,导致应用假死。通过jps和jmap工具排查发现,原因是运维人员未设置JVM参数,导致默认配置下每个应用占用近12G内存。最终通过调整JVM参数、优化堆内存大小等措施解决了问题。建议在生产环境中合理设置JVM参数,避免资源浪费和性能问题。
128 3
|
Java Spring
spring框架之AOP模块(面向切面),附带通知类型---超详细介绍
spring框架之AOP模块(面向切面),附带通知类型---超详细介绍
153 0
|
缓存 监控 Java
Spring框架之AOP(面向切面编程)
Spring框架之AOP(面向切面编程)
70 0