假设我们有如下的场景
public interface AddressRepo extends JpaRepository<Address,String>{
}
public interface EmailRepo extends JpaRepository<Email,String>{
}
@Service
public class TestService {
@Autowired
private EmailRepo emailRepo;
@Autowired
private AddressRepo addressRepo;
@Transactional
public void test() {
emailRepo.save(new Email("1","qq.com"));
addressRepo.save(new Address("1","地址1"));
}
}
AddressRepo/EmailRepo的save方法和TestService 的test方法因为都加了Transactional注解(请查看org.springframework.data.jpa.repository.support.SimpleJpaRepository类),这三个方法都会被事务拦截器org.springframework.transaction.interceptor.TransactionInterceptor拦截,每拦截一次,都会有一个TransactionInfo被创建,TransactionInfo包装了当前正在处理的事务的所有信息;
事务的处理从里往外,最里面的事务先处理完,接着处理外层的父事务,那么Spring是如何实现这个功能的呢?
在Spring TransactionAspectSupport类中还有个类TransactionInfo;以下是这个类的字段的定义
//当前事务使用的事务管理器
@Nullable
private final PlatformTransactionManager transactionManager;
// 当前事务的TransactionAttribute对象
@Nullable
private final TransactionAttribute transactionAttribute;
// joinpoint标识
private final String joinpointIdentification;
// TransactionStatus对象
@Nullable
private TransactionStatus transactionStatus;
// 当前事务信息的父事务信息
@Nullable
private TransactionInfo oldTransactionInfo;
前面几个字段都好理解,上面问题的答案,重点就是这个oldTransactionInfo字段,这个字段保存了当前事务所在的父事务上下文的引用,构成了一个链,准确的说是一个有向无环图;如上面的例子,构成的结构如下;
+------------------------------------------+
| |
| |
| |
| |
| TransactionInfo (AddressRepo#save) |
| | +-----------------------------------------------+
| oldTransactionInfo+ | | |
| | | | |
| | | | |
| | | | |
+------------------------------------------+ | TransactionInfo (TestService#test) |
| | |
+------------------------------------------------> |
+---------------------------------------------------> |
| | |
+------------------------------------------+ | |
| | | | |
| | | +-----------------------------------------------+
| TransactionInfo (Emai|Repo#save) |
| | |
| | |
| oldTransactionInfo |
| |
| |
| |
| |
| |
+------------------------------------------+
再来看一下TransactionInfo的两个核心方法是怎么使用oldTransactionInfo字段的,以及是怎么实现保存和恢复父事务的上下文的:
private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
new NamedThreadLocal<>("Current aspect-driven transaction");
//绑定当前正在处理的事务的所有信息到ThreadLocal
private void bindToThread() {
// Expose current TransactionStatus, preserving any existing TransactionStatus
// for restoration after this transaction is complete.
this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this);
}
// 当前事务处理完之后,恢复父事务上下文
private void restoreThreadLocalStatus() {
// Use stack to restore old transaction TransactionInfo.
// Will be null if none was set.
transactionInfoHolder.set(this.oldTransactionInfo);
}