上篇讲述了@Transtctional下调用@Async异步执行,然后token失效的问题:
记一次SpringCloud OpenFeign 服务调用传递 token @Async 上下文信息获取失败
本篇则详细讲述两者互存的各种场景及其使用注意事项。
一、@Transtctional @Async概念
@Transactional注解是Spring框架提供的一个用于声明式事务管理的注解。它可以应用在方法或类上,用于标识需要进行事务管理的方法或类。通过使用@Transactional注解,我们可以更加方便地管理事务,保障数据的一致性和可靠性
@Async 是 Spring 框架提供的注解,用于将方法标记为异步执行。通过使用 @Async 注解,可以告知 Spring 在调用被注解的方法时,使用新的线程或线程池进行异步执行。
二、调用场景复现,加入注解看是否回滚?
说明: @Transaction方法 服务于 saveInspectInfo() ;@Async 服务于testAsync()两者位于不同的两个类里边。
1)saveInspectInfo() 加注解;testAsync()无注解
@Transactional(rollbackFor = Exception.class)
@Override
public void saveInspectInfo() {
taskInspectMapper.updateDataStatus(126387703699750912L);
asyncTest.testAsync();
}
public void testAsync(){
log.info("testAsync threadName="+Thread.currentThread().getName());
TaskInspect taskInspect=new TaskInspect();
taskInspect.setProject("sss");
taskInspectMapper.insertSelective(taskInspect);
}
.updateDataStatus(126387703699750912L) 的dataStatus更新为3;但是执行第二个方法的时候失败了,最终结果 回滚成功,说明事物生效;
2)saveInspectInfo() 加注解;testAsync()也加注解
2-1) saveInspectInfo() 执行成功;testAsync() 执行失败,是否回滚?
@Async("asyncServiceExecutor")
public void testAsync(){
log.info("testAsync threadName="+Thread.currentThread().getName());
TaskInspect taskInspect=new TaskInspect();
taskInspect.setProject("sss");
taskInspectMapper.insertSelective(taskInspect);
}
我们可以看到 testAsync()开启了一个新的线程来执行的,最终结果是.updateDataStatus(126387703699750912L) 的dataStatus更新为3,回滚失败。
2-2)saveInspectInfo() 执行失败;testAsync() 执行成功,是否回滚?
@Transactional(rollbackFor = Exception.class)
@Override
public void saveInspectInfo() {
taskInspectMapper.updateDataStatus(126387703699750912L);
asyncTest.testAsync();
TaskInspect taskInspect=new TaskInspect();
taskInspect.setProject("sss");
taskInspectMapper.insertSelective(taskInspect);
}
@Async("asyncServiceExecutor")
public void testAsync(){
log.info("testAsync threadName="+Thread.currentThread().getName());
taskInspectMapper.updateDataStatus(126979559810678784L);
}
通过结果可以看到两个更新方法都走了,两个线程,猜测结果是咋样的?那肯定是一个成功一个失败呗。有事物的回滚,异步的执行成功。
2-3)saveInspectInfo() 执行失败,还未触发到异步操作。
@Transactional(rollbackFor = Exception.class)
@Override
public void saveInspectInfo() {
taskInspectMapper.updateDataStatus(126387703699750912L);
TaskInspect taskInspect=new TaskInspect();
taskInspect.setProject("sss");
taskInspectMapper.insertSelective(taskInspect);
asyncTest.testAsync();
}
testAsync()并未执行。所以两者状态都不变。
三、我就想让方法A执行完毕之后再异步执行方法B,如何处理?
就拿刚才的偏激场景来说事,我就想着方法A中的下单操作成功完成之后再走方法B,扣减库存。
@Transactional(rollbackFor = Exception.class)
@Override
public void saveInspectInfo() {
//事物操作方法1
taskInspectMapper.updateDataStatus(126387703699750912L);
//异步方法操作
asyncTest.testAsync();
//回滚
TaskInspect taskInspect = new TaskInspect();
taskInspect.setProject("sss");
//事物操作方法2
taskInspectMapper.insertSelective(taskInspect);
//事物操作方法3
taskInspectMapper.updateDataStatus(126389155885236224L);
}
如上代码,执行结果:操作方法1执行成功,但是回滚;异步操作执行成功;操作方法2失败导致的事物回滚;操作方法3未执行。
我想要的结果并非如此,想象的是,三个事物操作方法都执行成功了,才执行异步操作
// 给当前线程注册一个事务同步器
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int status) {
// 保证事务一定是已经提交了
if (TransactionSynchronization.STATUS_COMMITTED == status) {
// 异步方法写这里
.......
}
}
});
@Transactional(rollbackFor = Exception.class)
@Override
public void saveInspectInfo() {
//事物操作方法1
taskInspectMapper.updateDataStatus(126387703699750912L);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int status) {
// 保证事务一定是已经提交了
if (TransactionSynchronization.STATUS_COMMITTED == status) {
//异步方法操作
asyncTest.testAsync();
}
}
});
//回滚
TaskInspect taskInspect = new TaskInspect();
taskInspect.setProject("sss");
//事物操作方法2
taskInspectMapper.insertSelective(taskInspect);
//事物操作方法3
taskInspectMapper.updateDataStatus(126389155885236224L);
}
虽然按照顺序,我的异步注解在操作方法1后边,比较靠前,但是不影响我等待整个事物的成功执行后再提交。
四、两者碰撞注意事项
1)方法A和方法B不能同处于同一个类中。
因为 @Transactional 和 @Async 注解的实现都是基于 Spring 的 AOP ,而 AOP 的实现是基于动态代理实现的,故Async 失效的原理和原理是一样的。
所以要想两个注解都生效,必须保证是不同的类,保证是通过代理类去执行操作。
2)方法B尽量不要依赖方法A的相应操作。
举例说明:比如 方法A我执行了下单操作,想着用异步的方式去扣减库存,这种方式是严格禁止的,风险太大了。就是拿最偏激的场景来说一下这个事情。