文档说明本文主要针对同类不同类方法之间的异步调用详解。本文基于Spring Async 异步调用四种场景教学文档。1.所有入口方法 testMain() 均添加 Transactional 事务2.区分异步逻辑是否和主方法同一事务、是否跟随主方法回滚3.核心前置知识点Async 基于 AOP 代理生效同类this调用失效afterCommit 事务提交后才执行回调逻辑4.前置配置启动类添加 EnableAsync 开启异步支持。场景一A 类 testMain 事务内异步调用外部 B 类 testB11. 主业务类 ClassAimport org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; Service public class ClassA { Resource private ClassB classB; /** * 主入口方法开启事务 */ Transactional public void testMain() { // 1. 主线务数据库操作和testMain同事务 // 2. 异步调用外部B类方法 classB.testB1(); // 3. 其余业务操作 // 主线程仅将异步任务提交线程池无需阻塞等待异步逻辑执行完毕继续向下执行业务代码。 } }2.外部异步类 ClassBimport org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; Service public class ClassB { /** * 异步业务方法 * 独立线程、独立事务 */ Async Transactional public void testB1() { // 数据库操作、耗时业务逻辑 } }3.事务回滚说明若testMain中的业务于第一步的代码发生异常回滚testB1不会随testMain一起回滚testB1没执行。若testMain中的业务于第三步的代码发生了异常回滚则testB1已经执行了。testB1 内部报错testMain事务不受影响testMain数据正常提交。因为Async相当于开启了一个全新的线程两个之间互不影响。场景二、A 类 testMain 事务内afterCommit 回调中异步调用外部 B 类 testB11. 主业务类 ClassAimport org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import javax.annotation.Resource; Service public class ClassA { Resource private ClassB classB; Transactional public void testMain() { // 主线务数据库操作 // 注册事务提交后回调 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { Override public void afterCommit() { // 主线事务完全提交落库后才异步执行 classB.testB1(); } }); } }2.外部异步类 ClassBimport org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; Service public class ClassB { /** * 异步业务方法 * 独立线程、独立事务 */ Async Transactional public void testB1() { // 数据库操作、耗时业务逻辑 } }3.事务回滚说明若testMain中的业务代码发生异常回滚testB1压根不会执行因为afterCommit 回调仅在主线事务完整提交成功后才执行主线事务回滚时回调逻辑完全不执行。testB1 内部报错testMain事务不受影响testMain数据正常提交。场景三、A 类 testMain 事务内异步调用本类 3 个方法每次 getBean 获取代理调用本类方法直接用this调用会使异步注解失效。针对多个异步本类方法的调用建议不用获取getBean的方式调用频繁通过 getBean 获取代理对象存在重复容器查询开销多异步方法时不利于统一管控线程池参数。建议使用线程池管理调用。1. 主业务类ClassAimport org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; Service public class ClassA implements ApplicationContextAware { private ApplicationContext applicationContext; Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext applicationContext; } Transactional public void testMain() { // 1.主线务数据库操作 // 2.每次通过容器获取代理对象调用本类异步方法 ClassA proxy applicationContext.getBean(ClassA.class); proxy.testSub1(); proxy.testSub2(); proxy.testSub3(); // 3.其余业务操作 } Async Transactional public void testSub1() { // 异步业务1 } Async Transactional public void testSub2() { // 异步业务2 } Async Transactional public void testSub3() { // 异步业务3 } }2. 事务回滚说明若testMain中的业务于第一步的代码发生异常回滚testSub不会随testMain一起回滚testSub没执行。若testMain中的业务于第三步的代码发生了异常回滚testSub已经执行了。testB1 内部报错testMain事务不受影响testMain数据正常提交。因为Async相当于开启了一个全新的线程两个之间互不影响。场景四、A 类 testMain 事务内afterCommit 回调中异步调用本类 3 个方法1. 主业务类ClassAimport org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; Service public class ClassA implements ApplicationContextAware { private ApplicationContext applicationContext; Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext applicationContext; } Transactional public void testMain() { // 主线务数据库操作 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { Override public void afterCommit() { // 事务提交后获取代理执行本类异步方法 ClassA proxy applicationContext.getBean(ClassA.class); proxy.testSub1(); proxy.testSub2(); proxy.testSub3(); } }); } Async Transactional public void testSub1() {} Async Transactional public void testSub2() {} Async Transactional public void testSub3() {} }2. 事务回滚说明若testMain中的业务代码发生异常回滚testSub压根不会执行因为afterCommit 回调仅在主线事务完整提交成功后才执行主线事务回滚时回调逻辑完全不执行。testSub 内部报错testMain事务不受影响testMain数据正常提交。总结说明场景执行时机主线回滚后异步是否执行异步报错是否回滚主线场景 1 直接异步调用外部类主线执行中并行触发可能执行不回滚主线场景 2 afterCommit 异步外部类主线事务提交后触发不会执行不回滚主线场景 3 直接异步调用本类 (getBean)主线执行中并行触发可能执行不回滚主线场景 4 afterCommit 异步调用本类 (getBean)主线事务提交后触发不会执行不回滚主线使用建议1.若有数据库操作一类的建议使用场景22.针对多个异步本类方法的调用建议不用获取getBean的方式调用频繁通过 getBean 获取代理对象存在重复容器查询开销多异步方法时不利于统一管控线程池参数。建议使用线程池管理调用。
异步方法调用详解
发布时间:2026/7/2 6:00:22
文档说明本文主要针对同类不同类方法之间的异步调用详解。本文基于Spring Async 异步调用四种场景教学文档。1.所有入口方法 testMain() 均添加 Transactional 事务2.区分异步逻辑是否和主方法同一事务、是否跟随主方法回滚3.核心前置知识点Async 基于 AOP 代理生效同类this调用失效afterCommit 事务提交后才执行回调逻辑4.前置配置启动类添加 EnableAsync 开启异步支持。场景一A 类 testMain 事务内异步调用外部 B 类 testB11. 主业务类 ClassAimport org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; Service public class ClassA { Resource private ClassB classB; /** * 主入口方法开启事务 */ Transactional public void testMain() { // 1. 主线务数据库操作和testMain同事务 // 2. 异步调用外部B类方法 classB.testB1(); // 3. 其余业务操作 // 主线程仅将异步任务提交线程池无需阻塞等待异步逻辑执行完毕继续向下执行业务代码。 } }2.外部异步类 ClassBimport org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; Service public class ClassB { /** * 异步业务方法 * 独立线程、独立事务 */ Async Transactional public void testB1() { // 数据库操作、耗时业务逻辑 } }3.事务回滚说明若testMain中的业务于第一步的代码发生异常回滚testB1不会随testMain一起回滚testB1没执行。若testMain中的业务于第三步的代码发生了异常回滚则testB1已经执行了。testB1 内部报错testMain事务不受影响testMain数据正常提交。因为Async相当于开启了一个全新的线程两个之间互不影响。场景二、A 类 testMain 事务内afterCommit 回调中异步调用外部 B 类 testB11. 主业务类 ClassAimport org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import javax.annotation.Resource; Service public class ClassA { Resource private ClassB classB; Transactional public void testMain() { // 主线务数据库操作 // 注册事务提交后回调 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { Override public void afterCommit() { // 主线事务完全提交落库后才异步执行 classB.testB1(); } }); } }2.外部异步类 ClassBimport org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; Service public class ClassB { /** * 异步业务方法 * 独立线程、独立事务 */ Async Transactional public void testB1() { // 数据库操作、耗时业务逻辑 } }3.事务回滚说明若testMain中的业务代码发生异常回滚testB1压根不会执行因为afterCommit 回调仅在主线事务完整提交成功后才执行主线事务回滚时回调逻辑完全不执行。testB1 内部报错testMain事务不受影响testMain数据正常提交。场景三、A 类 testMain 事务内异步调用本类 3 个方法每次 getBean 获取代理调用本类方法直接用this调用会使异步注解失效。针对多个异步本类方法的调用建议不用获取getBean的方式调用频繁通过 getBean 获取代理对象存在重复容器查询开销多异步方法时不利于统一管控线程池参数。建议使用线程池管理调用。1. 主业务类ClassAimport org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; Service public class ClassA implements ApplicationContextAware { private ApplicationContext applicationContext; Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext applicationContext; } Transactional public void testMain() { // 1.主线务数据库操作 // 2.每次通过容器获取代理对象调用本类异步方法 ClassA proxy applicationContext.getBean(ClassA.class); proxy.testSub1(); proxy.testSub2(); proxy.testSub3(); // 3.其余业务操作 } Async Transactional public void testSub1() { // 异步业务1 } Async Transactional public void testSub2() { // 异步业务2 } Async Transactional public void testSub3() { // 异步业务3 } }2. 事务回滚说明若testMain中的业务于第一步的代码发生异常回滚testSub不会随testMain一起回滚testSub没执行。若testMain中的业务于第三步的代码发生了异常回滚testSub已经执行了。testB1 内部报错testMain事务不受影响testMain数据正常提交。因为Async相当于开启了一个全新的线程两个之间互不影响。场景四、A 类 testMain 事务内afterCommit 回调中异步调用本类 3 个方法1. 主业务类ClassAimport org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; Service public class ClassA implements ApplicationContextAware { private ApplicationContext applicationContext; Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext applicationContext; } Transactional public void testMain() { // 主线务数据库操作 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { Override public void afterCommit() { // 事务提交后获取代理执行本类异步方法 ClassA proxy applicationContext.getBean(ClassA.class); proxy.testSub1(); proxy.testSub2(); proxy.testSub3(); } }); } Async Transactional public void testSub1() {} Async Transactional public void testSub2() {} Async Transactional public void testSub3() {} }2. 事务回滚说明若testMain中的业务代码发生异常回滚testSub压根不会执行因为afterCommit 回调仅在主线事务完整提交成功后才执行主线事务回滚时回调逻辑完全不执行。testSub 内部报错testMain事务不受影响testMain数据正常提交。总结说明场景执行时机主线回滚后异步是否执行异步报错是否回滚主线场景 1 直接异步调用外部类主线执行中并行触发可能执行不回滚主线场景 2 afterCommit 异步外部类主线事务提交后触发不会执行不回滚主线场景 3 直接异步调用本类 (getBean)主线执行中并行触发可能执行不回滚主线场景 4 afterCommit 异步调用本类 (getBean)主线事务提交后触发不会执行不回滚主线使用建议1.若有数据库操作一类的建议使用场景22.针对多个异步本类方法的调用建议不用获取getBean的方式调用频繁通过 getBean 获取代理对象存在重复容器查询开销多异步方法时不利于统一管控线程池参数。建议使用线程池管理调用。