Spring @Async/@Transactional 失效的原因及解决方案

简介: 本来上一篇说要写关于AOP的实践的,但是这周刚好遇到了使用@Transactional和@Async的不生效的问题,这篇就进行一个回顾和记录。之前提到实现AOP的方法有动态代理、编译期,类加载期织入等等,Spring实现AOP的方法则就是利用了动态代理机制,正因如此,才会导致某些情况下@Async和@Transactional不生效。

本来上一篇说要写关于AOP的实践的,但是这周刚好遇到了使用@Transactional和@Async的不生效的问题,这篇就进行一个回顾和记录。

之前提到实现AOP的方法有动态代理、编译期,类加载期织入等等,Spring实现AOP的方法则就是利用了动态代理机制,正因如此,才会导致某些情况下@Async和@Transactional不生效。

我们已@Aysnc为例,在Spring boot中使用Async很简单:

@EnableAsync //添加此注解开启异步调用
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

当某些任务执行时间较长,且客户端不需要及时获取结果(如调用第三方API),只要在需要异步调用的任务上添加 @Async即可,如:

@Async
void asyncTask(String keyword) {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        //logger
        //error tracking
    }
    System.out.println(keyword);
}

这样asyncTask就会异步执行。 然而,如果在同一个Class内,出现下面这样的情况,先调用一个非异步任务:

private void noAsyncTask(String keyword){
    asyncTask(keyword); //该方法内再调用异步方法
}

@Async
void asyncTask(String keyword) {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        //logger
        //error tracking
    }
    System.out.println(keyword);
}

此时,@Async是没有生效的。 原因就是@Async和@Transaction利用了动态代理机制。

当Spring发现@Transactional或者@Async时,会自动生成一个ProxyObject,如:


img_faff7aa745fa0c4753fd1ec9869fcda1.png
ProxyObject

此时调用Class.transactionTask会调用ProxyClass.产生事务操作。
然而当Class里的一个非事务方法调用了事务方法,ProxyClass是这样的:


img_17d3b23e81bc2c16fca0ce5ffdbecee9.png
ProxyObject with noTransactionTask

到这里应该可以看明白了,如果调用了noTransactionTask方法,最终会调用到Class.transactionTask,而这个方法是不带有任何Transactional的信息的,也就是@Transactional根本没有生效哦。
简单来说就是: 同一个类内这样调用的话,只有第一次调用了动态代理生成的ProxyClass,之后一直用的是不带任何切面信息的方法本身。

知道了原因,处理方法也特别简单,就是让noTransactionTask里依旧调用ProxyClass的transactionTask方法:只需要显示利用Spring暴露的AopContext即可。代码如下:

private void noAsyncTask(String keyword){
    // 注意这里 调用了代理类的方法
    ((YourClass) AopContext.currentProxy()).asyncTask(keyword);
}

@Async
void asyncTask(String keyword) {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        //logger
        //error tracking
    }
    System.out.println(keyword);
}

记得要在Class上加上@EnableAspectJAutoProxy(exposeProxy = true)来暴露AOP的Proxy对象才行,否则会报错。

或者就可以把这样的方法放到另外一个类里,不要产生类里一个非异步/非事务方法,调用了异步/事务方法,不过大家协同开发同一个文件的话,谁能保证没有人这样调用呢?总而言之无论什么方案,都是使得调用ProxyObject的方法。

如果有本篇任何错误,或者更好的解决方案,欢迎评论私信,我会一一回复。

目录
相关文章
|
11月前
|
人工智能 监控 安全
智慧工地解决方案,Spring Cloud智慧工地源代码
智慧工地平台针对建筑工地人员管理难、机械设备繁多、用电安全及施工环境复杂等问题,通过集成应用和硬件设备,实现数据互联互通与集中展示。基于微服务架构(Java+Spring Cloud+UniApp+MySql),平台支持PC端、手机端、平板端、大屏端管理,涵盖人员实名制、工资考勤、视频AI监控、绿色施工、危大工程监测、物料管理和安全质量管理等功能,助力施工现场的数字化、智能化综合管理,提升效率与安全性。
259 15
|
7月前
|
druid Java 关系型数据库
Spring Boot与Druid升级解决方案
好的,我需要帮助用户解决他们遇到的数据库连接问题,并升级项目的依赖。首先,用户提供的错误信息是关于Spring Boot应用在初始化数据源时抛出的异常,具体是Druid连接池验证连接失败。同时,用户希望升级项目的依赖版本。
729 10
|
8月前
|
Java 关系型数据库 MySQL
深入解析 @Transactional——Spring 事务管理的核心
本文深入解析了 Spring Boot 中 `@Transactional` 的工作机制、常见陷阱及最佳实践。作为事务管理的核心注解,`@Transactional` 确保数据库操作的原子性,避免数据不一致问题。文章通过示例讲解了其基本用法、默认回滚规则(仅未捕获的运行时异常触发回滚)、因 `try-catch` 或方法访问修饰符不当导致失效的情况,以及数据库引擎对事务的支持要求。最后总结了使用 `@Transactional` 的五大最佳实践,帮助开发者规避常见问题,提升项目稳定性与可靠性。
1339 12
|
8月前
|
监控 Java 关系型数据库
Spring Boot整合MySQL主从集群同步延迟解决方案
本文针对电商系统在Spring Boot+MyBatis架构下的典型问题(如大促时订单状态延迟、库存超卖误判及用户信息更新延迟)提出解决方案。核心内容包括动态数据源路由(强制读主库)、大事务拆分优化以及延迟感知补偿机制,配合MySQL参数调优和监控集成,有效将主从延迟控制在1秒内。实际测试表明,在10万QPS场景下,订单查询延迟显著降低,超卖误判率下降98%。
372 5
|
8月前
|
SQL 前端开发 Java
深入分析 Spring Boot 项目开发中的常见问题与解决方案
本文深入分析了Spring Boot项目开发中的常见问题与解决方案,涵盖视图路径冲突(Circular View Path)、ECharts图表数据异常及SQL唯一约束冲突等典型场景。通过实际案例剖析问题成因,并提供具体解决方法,如优化视图解析器配置、改进数据查询逻辑以及合理使用外键约束。同时复习了Spring MVC视图解析原理与数据库完整性知识,强调细节处理和数据验证的重要性,为开发者提供实用参考。
364 0
|
8月前
|
安全 前端开发 Java
Spring Boot 项目中触发 Circular View Path 错误的原理与解决方案
在Spring Boot开发中,**Circular View Path**错误常因视图解析与Controller路径重名引发。当视图名称(如`login`)与请求路径相同,Spring MVC无法区分,导致无限循环调用。解决方法包括:1) 明确指定视图路径,避免重名;2) 将视图文件移至子目录;3) 确保Spring Security配置与Controller路径一致。通过合理设定视图和路径,可有效避免该问题,确保系统稳定运行。
572 0
|
10月前
|
网络协议 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-优雅草卓伊凡解决方案
691 7
|
Java Nacos Sentinel
Spring Cloud Alibaba:一站式微服务解决方案
Spring Cloud Alibaba(简称SCA) 是一个基于 Spring Cloud 构建的开源微服务框架,专为解决分布式系统中的服务治理、配置管理、服务发现、消息总线等问题而设计。
2536 13
Spring Cloud Alibaba:一站式微服务解决方案
|
12月前
|
运维 监控 Java
为何内存不够用?微服务改造启动多个Spring Boot的陷阱与解决方案
本文记录并复盘了生产环境中Spring Boot应用内存占用过高的问题及解决过程。系统上线初期运行正常,但随着业务量上升,多个Spring Boot应用共占用了64G内存中的大部分,导致应用假死。通过jps和jmap工具排查发现,原因是运维人员未设置JVM参数,导致默认配置下每个应用占用近12G内存。最终通过调整JVM参数、优化堆内存大小等措施解决了问题。建议在生产环境中合理设置JVM参数,避免资源浪费和性能问题。
912 3
|
安全 Java API
实现跨域请求:Spring Boot后端的解决方案
本文介绍了在Spring Boot中处理跨域请求的三种方法:使用`@CrossOrigin`注解、全局配置以及自定义过滤器。每种方法都适用于不同的场景和需求,帮助开发者灵活地解决跨域问题,确保前后端交互顺畅与安全。
1751 0

热门文章

最新文章