为了能在我们的线程中使用事务,我们需要了解Spring中事务的工作机制。Spring中,事务信息存储在ThreadLocal
变量中。因此,这些变量是某个线程上进行的事务所特有的(译注:这些变量对于其他线程中发生的事务来讲是不可见的,无关的)。
单线程的情况下,一个事务会在层级式调用的Spring组件之间传播。
但是在@Transactional
注解的服务方法会产生一个新的线程的情况下,事务是不会从调用者线程传播到新建线程的。结果会是被调用者线程会说缺少事务(译注:这里指的应该是被调用者线程需要事务的情况,当然如果被调用者线程根本不需要事务,那它也不会说缺少事务的)。
如果被调用者线程发生的动作需要数据库访问比如通过JPA,那必须要创建一个事务。
通过查看注解@Transactional
的文档我们可以获取更多关于事务传播类型的信息。@Transactional
默认的传播模式是 REQUIRED
。
因此通过在使用异步注解@Async
的方法上再加上注解@Transactional
,这个在新线程中执行的异步任务上面,就会有一个新的事务被创建,而且它能够传播到在新线程中调用的其他服务方法上。
例子,我们的异步方法也可以再添加一个注解@Transactional
来表明它需要事务:
// 一个异步方法,会使用一个新的事务,该事务跟该方法调用者是否使用事务,如何使用事务无关
@Async
@Transactional
public void executeTransactionally() {
System.out.println("该逻辑在任务执行器线程中使用一个新的事务执行");
}
上面所讲的机制同样适用于使用Runnable
实例时从其run()中调用异步方法的场景。异步注解@Async
看起来简单易用,其实背后它还是被封装成了一个Runnable
对象然后分发给了一个执行器对象。
总结一下,在Spring中使用异步线程和事务时,我们应该额外小心:需要谨记事务不会跨线程传播。最后,还要注意你使用@Async
和@Transactional
注解的方法要是public
的,这样才能确保它被调用时通过了对其执行一些必要动作的代理层(译注:比如代理会执行事务创建和提交等动作)。