使用 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 它实际上可以工作。
在 Spring 5 中,异步通知总是首先执行。请参阅 AsyncAnnotationBeanPostProcessor
public AsyncAnnotationBeanPostProcessor() {
setBeforeExistingAdvisors(true);
}
之后,当顾问应用代码执行时,在 postProcessAfterInitialization 中的超类上
if (this.beforeExistingAdvisors) {
advised.addAdvisor(0, this.advisor);
}
@EnableTransactionManagement#order javadoc 说
指示交易顾问执行的顺序
但在@EnableAsync
指示 AsyncAnnotationBeanPostProcessor 的应用顺序。
如果您在旧版本的 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);
}
}