1:事务原理
1.1:aop/动态代理
类路径:org/springframework/aop/framework/CglibAopProxy.java
ReflectiveMethodInvocation#proceed 后续:
作用:采用aop/动态代理的作用是为了在调用@Transactional 注解修饰的方法之前,对目标方法做一次增强。
1.2:threadLocal
作用:采用ThreadLocal的作用是用来存储当前线程的事务节点信息。
1.3:事务核心代码
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
/**
* 每个被 @Transactional 修饰的方法都会走一遍 transaction interceptor,然后新增一个事务节点。
* 每个事务节点执行前都会判断是否需要新建事务、开启事务。
**/
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// 创建一个事务信息对象,每一次调用 @Transactional 注解修饰的方法,都会重新创建一个 TransactionInfo 对象。
// 若有调用链有多个 @TransactionInfo 修饰的方法,则会形成一个事务链。
// 将最新的事务节点信息通过 threadLocal 更新到当前线程 。
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal;
try {
// 真正执行crud语句的过程
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 抛异常之后决定是否回滚还是继续提交
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 清除当前节点的事务信息,将旧事务节点信息通过 threadLocal 更新到当前线程。
cleanupTransactionInfo(txInfo);
}
// 事务链执行完毕之后
commitTransactionAfterReturning(txInfo);
return retVal;
}else {
...
}
}
org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo
protected final class TransactionInfo {
// 事务管理器
@Nullable
private final PlatformTransactionManager transactionManager;
@Nullable
private final TransactionAttribute transactionAttribute;
// 切点信息(类路径#方法名称)
private final String joinpointIdentification;
// 当前事务节点状态(是否完成、)
@Nullable
private TransactionStatus transactionStatus;
// 旧事务节点/前事务节点
@Nullable
private TransactionInfo oldTransactionInfo;
}
1.4:事务链-图
2:事务回滚场景
用两个Service进行测试:
/**
* 模拟 Service A
**/
@Service
public class AopiService {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Resource(name = AopiRepositry.PACKAGE_BEAN_NAME)
private AopiRepositry aopiRepositry;
@Resource
private PmsTestService pmsTestService;
@Resource
private AopiService aopiService;
...
}
/**
* 模拟 Service B
**/
@Service
public class PmsTestService {
@Transactional(rollbackFor = Exception.class)
public void insertWithTransactional() {
int i = 1 / 0;
}
public void insertWithoutTransactional() {
int i = 1 / 0;
}
}
模拟场景:
1:模拟ServiceA调用ServiceB(会异常,被try-catch),这种情况会回滚吗?
2:模拟ServiceA中调用自己的本类中的方法(会异常,被try-catch),这种情况会回滚吗?
3:模拟ServiceA注入自己并通过依赖的ServiceA调用另一个方法(会异常,被try-catch),这种情况会回滚吗?
2.1:场景1-1
/**
* serviceA 加了 @Transactional
* serviceB 加了 @Transactional
* 最终:回滚
**/
@Transactional(rollbackFor = Exception.class)
public void insertA() {
Aopi aopi = new Aopi();
aopi.setName("1");
aopi.setAge(23);
aopiRepositry.insert(aopi);
try {
pmsTestService.insertWithTransactional();
} catch (Exception e) {
log.error("插入报错", e);
}
// 判断事务是否回滚
if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
log.info("事务回滚了");
} else {
log.info("事务没回滚");
}
}
2.2:场景1-2
/**
* serviceA 加了 @Transactional
* serviceB 没加 @Transactional,但是手动 throw e;
* 最终:回滚
**/
@Transactional(rollbackFor = Exception.class)
public void insertAA() {
Aopi aopi = new Aopi();
aopi.setName("1");
aopi.setAge(23);
aopiRepositry.insert(aopi);
try {
pmsTestService.insertWithoutTransactional();
} catch (Exception e) {
log.error("插入报错", e);
throw e;
}
}
2.3:场景1-3
/**
* serviceA 加了 @Transactional
* serviceB 没加 @Transactional,但是手动 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
* 最终:回滚
* <p>
* 若不手动 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(),那么不会回滚
**/
@Transactional(rollbackFor = Exception.class)
public void insertAAA() {
Aopi aopi = new Aopi();
aopi.setName("1");
aopi.setAge(23);
aopiRepositry.insert(aopi);
try {
pmsTestService.insertWithoutTransactional();
} catch (Exception e) {
log.error("插入报错", e);
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
// 判断事务是否回滚
if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
log.info("事务回滚了");
} else {
log.info("事务没回滚");
}
}
2.4:场景2-1
/**
* serviceA 加了 @Transactional
* 调用过程中被异常被捕获,并不抛出
* 最终:不回滚
**/
@Transactional(rollbackFor = Exception.class)
public void insertAAAA() {
Aopi aopi = new Aopi();
aopi.setName("1");
aopi.setAge(23);
aopiRepositry.insert(aopi);
try {
int i = 1 / 0;
} catch (Exception e) {
log.error("插入报错", e);
}
// 判断事务是否回滚
if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
log.info("事务回滚了");
} else {
log.info("事务没回滚");
}
}
2.5:场景2-2
/**
* 本类方法A 加了 @Transactional
* 本类方法B 加了 @Transactional,异常被捕获,并不抛出
* 最终:不回滚
* <p>
* 原因:调用 insert 并不会重新走代理调用(this 对象不是代理对象)
**/
@Transactional(rollbackFor = Exception.class)
public void insertAAAAA() {
Aopi aopi = new Aopi();
aopi.setName("1");
aopi.setAge(23);
aopiRepositry.insert(aopi);
try {
insert();
} catch (Exception e) {
log.error("插入报错", e);
}
// 判断事务是否回滚
if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
log.info("事务回滚了");
} else {
log.info("事务没回滚");
}
}
2.6:场景3-1
/**
* 本类方法A 加了 @Transactional
* 自己注入自己,再调用本类方法B,加了 @Transactional,异常被捕获,并不抛出
* 最终:回滚
* <p>
* 原因:aopiService bean 是一个代理bean,每次调用都会重新走代理调用逻辑。
**/
@Transactional(rollbackFor = Exception.class)
public void insertAAAAAA() {
Aopi aopi = new Aopi();
aopi.setName("1");
aopi.setAge(23);
aopiRepositry.insert(aopi);
try {
aopiService.insert();
} catch (Exception e) {
log.error("插入报错", e);
}
// 判断事务是否回滚
if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
log.info("事务回滚了");
} else {
log.info("事务没回滚");
}
}
@Transactional(rollbackFor = Exception.class)
public void insert() {
int i = 1 / 0;
}
3:结论
1:程序异常,事务是否回滚取决于 当前线程的事务状态。
2:异常是否抛出并不是并不是一定会导致回滚失败的原因。即使异常被捕获,且不再次throw,事务也可能回滚。
3:抛出的异常不在rollback 范围内,也不会进行回滚。
其他:
1:spring 用的cglib代理库是自己的库(依赖于spring-aop的包),并没有引用第三方cglib库。