@Async 和 @Transaction 方面顺序

问题描述 投票:0回答:2

使用 Spring Boot 2.1.1.RELEASE / Spring Framework 5.1.4,我有一个通过以下方式启用了

@Async
@Transactional
注释的应用程序:

@EnableAsync(mode = AdviceMode.ASPECTJ)
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)

运行带有两者注解的方法时,首先创建事务,然后开始异步执行。因此,实际的方法体不会在事务内执行。

    @Transactional
    @Async
    public void myAsyncMethod() {
        // asynchronous database stuff
    }

我如何配置 spring / 方面以按有意义的顺序实际执行,例如在新线程上启动事务?

顺便说一句,使用较旧的 Spring Boot 1.5.17 / Spring Framework 4.3.20 它实际上可以工作。

演示:https://github.com/jaarts/spring-asynctransaction-demo

spring spring-transactions spring-aspects spring-async
2个回答
2
投票

在 Spring 5 中,异步通知总是首先执行。请参阅 AsyncAnnotationBeanPostProcessor

public AsyncAnnotationBeanPostProcessor() {
    setBeforeExistingAdvisors(true);
}

之后,当顾问应用代码执行时,在 postProcessAfterInitialization 中的超类上

if (this.beforeExistingAdvisors) {
   advised.addAdvisor(0, this.advisor);
}

@EnableTransactionManagement#order javadoc 说

指示交易顾问执行的顺序

但在@EnableAsync

指示 AsyncAnnotationBeanPostProcessor 的应用顺序。


0
投票

如果您在旧版本的 spring 中使用 aspectj 和编译时编织(ctw),唯一的解决方案是添加一个 Aspect 并使用 DeclarePrecedence,如下所示:

package org.example.config;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclarePrecedence;
   
@Aspect
@DeclarePrecedence(
  "org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect" 
  + "," + 
  "org.springframework.transaction.aspectj.AnnotationTransactionAspect"
)

public class AspectOrdering {}

对于较新版本的 spring(至少是 v6+),这不再需要了。测试:

public class AspectOrderingTest {

  @Test
  public void verifyExecutionOrdering() throws ExecutionException, InterruptedException {
    verifyExecutionOrder().get();
  }

  @Async
  @Transactional
  public Future<Boolean> verifyExecutionOrder() {
    var stackTrace = Thread.currentThread().getStackTrace();
    List<String> executionOrder = new ArrayList<>();
    for (var stackTraceElement : stackTrace) {
        var className = stackTraceElement.getClassName();
        if (className.equals("org.springframework.transaction.aspectj.AbstractTransactionAspect")) {
            if (!executionOrder.contains("Transaction")) {
                executionOrder.add("Transaction");
            }
        }
        if (className.equals("org.springframework.scheduling.aspectj.AbstractAsyncExecutionAspect")) {
            if (!executionOrder.contains("Async")) {
                executionOrder.add("Async");
            }
        }
    }
    // first in Stacktrace, late in executiuon
    Collections.reverse(executionOrder);
    assertEquals("Async", executionOrder.get(0));
    assertEquals("Transaction", executionOrder.get(1));
    return CompletableFuture.completedFuture(true);
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.