1:事務原理
1.1:aop/動态代理
類路徑:org/springframework/aop/framework/CglibAopProxy.java
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iYkNDOmZjM0UjYkdDM2MTN2cTY0AzMhdDM3MmMzQ2Ym9CXyAzLclDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL5M3Lc9CX6MHc0RHaiojIsJye.png)
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,事務也可能復原。