首先看@Transactional,这个annotation是用来捆绑事务的
场景
2个账户,A往B账户打100元,那么A账户减少100(动作1),同时B账户增加100(动作2).
这两个动作是不可拆分的,动作1完成后如果出了错,比如说B账户被冻结,那么这个时候应当进行将钱退回到A账户,这个行为叫回滚。
动作1,动作2相继完成后,就有了回执单,相当于这个事务完成了。
应用
以Spring Boot+jpa为例,看底层代码是如何实现的
- 引包:spring-boot-autoconfigure或者包含这个包的
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<--包含这个包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<--包含这个包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
自动配置中有个配置事务的类 TransactionAutoConfiguration,可以看到jpa采用的是PlatformTransactionManager
@Configuration
@ConditionalOnClass({PlatformTransactionManager.class})
@AutoConfigureAfter({JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class})
@EnableConfigurationProperties({TransactionProperties.class})
public class TransactionAutoConfiguration {
...
}
@AutoConfigureAfter 在加载配置的类之后再加载当前类
@ConditionalOnClass判断当前classpath下是否存在指定类,若是则将当前的配置装载入spring容器
这里加载的同包下的 JpaTransactionManager
JpaTransactionManager 通过继承 JpaTransactionManager 实现具体事务(获取底层transaction,进行数据库连接,提交回滚等操作)
那么,@Transactional是怎么启用的?
当然是通过拦截器org.springframework.transaction.interceptor.TransactionInterceptor
TransactionInterceptor 继承 MethodInterceptor 拦截方法,在方法的外围包一层事务
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
invokeWithinTransaction方法会将事务与当前线程绑定
- 创建事务信息TransactionInfo(PlatformTransactionManager, TransactionAttribute, String joinpointIdentification),设置初始状态
- TransactionDefinition 存放@Transactional中对应的信息
- 若有异常调用completeTransactionAfterThrowing
- 若没有 commitTransactionAfterReturning
- 最后清除事务信息 cleanupTransactionInfo
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}