spring原始碼解析 (七) 事務底層原始碼實現
1.spring事務的本質
資料庫中的事務預設存在,只不過每次都自動提交了,資料庫中這個引數--autoCommitd=true
其本質是 begin sql commit
而spring事務其本質是資料庫一致,也是begin sql commit ,只不過將autoCommitd設定為了false。
而每個事務其實都是一個資料庫連線,根據不同的事務傳播機制進行掛起,儲存點等操作。
spring事務執行還是通過代理織入的
一個小demo:如果物件直接呼叫自己的方法,那麼這個@Transactional事務就不會觸發,因為事務執行的本質還是代理,直接呼叫就是原始物件方法的呼叫,沒有進行事務的代理邏輯,但如果自己注入自己,換成txService.a(),事務代理邏輯就會觸發,因為txService屬於代理物件,進行了代理邏輯的織入。
@Component
public class TxService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private TxService txService;
public void insert11(){
System.out.println("insert11");
// txService.a();
a();
}
@Transactional
public void a(){
System.out.println("A");
jdbcTemplate.execute("insert grade values(13,2,2,2)");
int i =100/0;
}
}
2.事務底層原始碼分析
首先引入了@EnableTransactionManagement
裡面註冊了兩個bean,AutoProxyRegistrar和ProxyTransactionManagementConfiguration
AutoProxyRegistrar主要作用是開啟自動代理
ProxyTransactionManagementConfiguration中定義了三個bean:
- BeanFactoryTransactionAttributeSourceAdvisor:一個PointcutAdvisor
- AnnotationTransactionAttributeSource:就是Pointcut
- TransactionInterceptor:就是代理邏輯Advice
(TransactionAttributeSource其實就是代表一個@Transactional)
接下來我們檢視一下事務的執行原始碼。
事務執行原始碼路徑:也是通過Interceptor進行實現的。
TransactionInterceptor.invoke()-》invokeWithinTransaction()
檢視invokeWithinTransaction,該方法為事務執行的流程。
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();
// 獲取到當前方法或類上的@Transactional註解的資訊
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 得到一個TransactionManager
final TransactionManager tm = determineTransactionManager(txAttr);
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new TransactionUsageException(
"Unsupported annotated transaction on suspending function detected: " + method +
". Use TransactionalOperator.transactional extensions instead.");
}
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
if (adapter == null) {
throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
method.getReturnType());
}
return new ReactiveTransactionSupport(adapter);
});
return txSupport.invokeWithinTransaction(
method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
}
// 轉化為PlatformTransactionManager
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// 根據當前執行的類中的某個方法以及@Transactional註解的資訊生成一個唯一標誌,這個標記會用來作為事務名
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 建立事務,並得到事務資訊,後面需要事務資訊用來進行提交或回滾,這裡不一定建立了事務,也可能延用了已存在的事務
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
// 執行業務方法邏輯
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
// 出現異常的情況下,如果異常需要rollback,則回滾,否則則會提交
completeTransactionAfterThrowing(txInfo, ex);
// 出現異常後會先執行finally中的方法邏輯在丟擲當前異常
throw ex;
}
finally {
// 清除ThreadLocal中的事務資訊
cleanupTransactionInfo(txInfo);
}
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
// 提交事務
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
try {
Object retVal = invocation.proceedWithInvocation();
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
return retVal;
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
}
}
接下來進入createTransactionIfNecessary方法檢視事務的建立過程。
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
// 事務的名字
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
// 事務狀態物件,
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
//獲得事務
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
進入AbstractPlatformTransactionManager.getTransaction()方法
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
// Use defaults if no transaction definition given.
// TransactionDefinition就是TransactionAttribute,就是@Transactional註解資訊
// 表示一個事務的定義,通過@Transactional註解也就是在定義一個事務
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
// 獲取一個事務物件,每次都會生成一個DataSourceTransactionObject,而重點是這個物件中是否已經存在一個資料庫連線
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
// 如果已經存在一個資料庫連線,表示當前執行緒已經存在一個事務
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
// 存在事務的情況下,按不同的傳播級別進行處理
return handleExistingTransaction(def, transaction, debugEnabled);
}
// Check definition settings for new transaction.
// timeout如果為-1,表示沒有時間限制
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
// 當前執行緒還不存在事務
// No existing transaction found -> check propagation behavior to find out how to proceed.
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 掛起
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 事務物件中包括:
// 1. 事務的定義
// 2. 事務物件
// 3. 是否是新事務
DefaultTransactionStatus status = newTransactionStatus(
def, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 開啟事務,獲得新的Connection物件
doBegin(transaction, def);
// 初始化TransactionSynchronizationManager中的屬性
prepareSynchronization(status, def);
return status;
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
else {
// 不會doBegin,不會真的開啟事務,也就是不會把Connection的autoCommit設定為false,sql沒有在事務中執行
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + def);
}
//
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
繼續向裡面走,看核心方法DataSourceTransactionManager.doGetTransaction(),這裡就是建立了一個DataSourceTransactionObject物件
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
// 是否允許巢狀事務,預設是允許的,在DataSourceTransactionManager無參構造方法中進行了設定
txObject.setSavepointAllowed(isNestedTransactionAllowed());
// 從當前執行緒ThreadLocal中獲取當前DataSource所對應的資料庫連線,可能為null
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
回到流程中去,到 doBegin方法,開啟一個事務
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 如果事務物件內沒有連線就從dataSource中獲取一個連線
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// isSynchronizedWithTransaction 我的理解,表示連線是否和事務同步,表示每開啟一個事務就從DataSource中獲取一個連線
// 預設是false,所以在開啟事務時,除非當前事務物件中還沒有資料庫連線才會從DataSource中去獲取一個連線
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
// 設定為newConnectionHolder為true
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 如果在開啟事務時,事務物件中已經有資料庫連線了
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
// 設定資料庫連線的隔離級別
// 如果當前事務中的隔離級別跟資料庫的隔離級別不一樣就返回資料庫的隔離級別並記錄下來,事務結束後恢復
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
// 把資料庫連線的autoCommit設定為false,禁止資料庫的自動提交
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
// 設定資料庫連線的超時時間
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
// 把新生成的資料庫連線設定到當前執行緒的TheadLocal中進行快取
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
從一個小demo中總結一下流程:
@Component
public class TxService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private TxService txService;
@Transactional
public void insert11(){
System.out.println("insert11");
jdbcTemplate.execute("insert grade values(11,2,2,2)");
txService.a();
}
@Transactional(propagation = Propagation.REQUIRES_NEW) 掛起test,開啟新事務,執行a
//@Transactional(propagation = Propagation.NOT_SUPPORTED) 掛起test,不開啟事務,執行a
public void a(){
jdbcTemplate.execute("insert grade values(13,2,2,2)");
}
}
事務執行過程:
- 生成test事務狀態物件
- test事務doBegin,獲取並將資料庫連線2825設定到test事務狀態物件中
- 把test事務資訊設定到事務同步管理器中
- 執行test業務邏輯方法(可以獲取到test事務的資訊)
-
- 生成a事務狀態物件,並且可以獲取到當前執行緒中已經存在的資料庫連線2825
- 判斷出來當前執行緒中已經存在事務
- 如果需要新開始事務,就先掛起資料庫連線2825,掛起就是把test事務資訊從事務同步管理器中轉移到掛起資源物件中,並把當前a事務狀態物件中的資料庫連線設定為null
- a事務doBegin,新生成一個資料庫連線2826,並設定到a事務狀態物件中
- 把a事務資訊設定到事務同步管理器中
- 執行a業務邏輯方法(可以利用事務同步管理器獲取到a事務資訊)
- 利用a事務狀態物件,執行提交
- 提交之後會恢復所掛起的test事務,這裡的恢復,其實只是把掛起資源物件中所儲存的資訊再轉移回事務同步管理器
5.繼續執行test業務邏輯方法(仍然可以獲取到test事務的資訊)
6.利用test事務狀態物件,執行提交
關於事務的掛起和恢復說明一下:spring事務的掛起就是把事務管理同步器中的資訊轉移到掛起資源物件,恢復就是把掛起資源物件轉移回事務同步管理器中。
事務傳播型別
事務傳播機制只針對於同一執行緒,如果是多執行緒,則各使用各的事務。
相關文章
- Spring 事務原始碼解析Spring原始碼
- ThreadLocal底層原始碼解析thread原始碼
- Java集合類,從原始碼解析底層實現原理Java原始碼
- Spring原始碼剖析9:Spring事務原始碼剖析Spring原始碼
- 七、真正的技術——CAS操作原理、實現、底層原始碼原始碼
- 【spring原始碼】十一、事務Spring原始碼
- 【spring】事務底層的實現流程Spring
- Seata原始碼分析(一). AT模式底層實現原始碼模式
- 筆記-runtime原始碼解析之讓你徹底瞭解底層原始碼筆記原始碼
- Spring事務原始碼解讀Spring原始碼
- HashMap底層資料結構原始碼解析HashMap資料結構原始碼
- Spring原始碼剖析8:Spring事務概述Spring原始碼
- PandasTA 原始碼解析(七)AST原始碼
- HasMap 底層原始碼分析ASM原始碼
- 持久層Mybatis3底層原始碼分析,原理解析MyBatisS3原始碼
- iOS底層原理總結 -- 利用Runtime原始碼 分析Category的底層實現iOS原始碼Go
- Spring原始碼深度解析(郝佳)-學習-原始碼解析-Spring MVCSpring原始碼MVC
- spring原始碼解析:元註解功能的實現Spring原始碼
- Java併發集合類ConcurrentHashMap底層核心原始碼解析JavaHashMap原始碼
- Spring事務原始碼分析專題(一)JdbcTemplate使用及原始碼分析Spring原始碼JDBC
- Spring AOP 原始碼解析Spring原始碼
- Spring原始碼之IOC(一)BeanDefinition原始碼解析Spring原始碼Bean
- 使用Spring Boot + Kafka實現Saga分散式事務模式的原始碼 - vinsguruSpring BootKafka分散式模式原始碼
- 深入分析Java中的PriorityQueue底層實現與原始碼Java原始碼
- SpringBoot原始碼分析之條件註解的底層實現Spring Boot原始碼
- React Native 0.55.4 Android 原始碼分析(Java層原始碼解析)React NativeAndroid原始碼Java
- 深度解析Spring Cloud Ribbon的實現原始碼及原理SpringCloud原始碼
- spring原始碼深度解析— IOC 之 容器的基本實現Spring原始碼
- JAVA ArrayList集合底層原始碼分析Java原始碼
- Owin Katana 的底層原始碼分析原始碼
- PHP 底層原始碼下載地址PHP原始碼
- Spring Cloud OAuth 微服務內部Token傳遞的原始碼實現解析SpringCloudOAuth微服務原始碼
- spring事務管理原始碼分析(二)事務處理流程分析Spring原始碼
- Spring Security原始碼分析六:Spring Social社交登入原始碼解析Spring原始碼
- Spring之事務原始碼Spring原始碼
- Spring的JDK動態代理如何實現的(原始碼解析)SpringJDK原始碼
- Spring5原始碼解析_IOC之容器的基本實現Spring原始碼
- Spring 原始碼解讀第七彈!bean 標籤的解析Spring原始碼Bean