Spring事務實現原理
阿新 • • 發佈:2020-09-09
## 前言
對於一個應用而言,事務的使用基本是不可避免的。雖然Spring給我們提供了開箱即用的事務功能——`@Transactional`。
但是,自帶的事務功能卻也存在控制粒度不夠的缺點。更糟糕的是,`@Transactional`在某些情況下就失效了。可能一些讀者baidu/google一下解決辦法後,失效的問題確實解決了。但是由於不瞭解底層的原理,這樣的問題可能在今後的工作中往復出現。
本文就為大家揭開`@Transactional`下的祕密。
## 原生的事務管理
在沒有Spring存在的時候,事務就已經誕生了。其實框架依賴的還是底層提供的能力,只不過它對這一過程的抽象和複用。
這裡我們用底層的API來了解下事務管理的過程(JDBC為例):
```java
// 獲取mysql資料庫連線
Connection conn = DriverManager.getConnection("xxxx");
conn.setAutoCommit(false);
statement = conn.createStatement();
// 執行sql,返回結果集
resultSet = statement.executeQuery("xxxx");
conn.commit(); //提交
//conn.rollback();//回滾
```
上面是一個原生操作事務的一個例子,這些過程也是Spring事務逃不開的,只不過在為了程式設計的效率讓這一過程自動化或是透明化的你無法感知罷了。
而我們之後做的就是逐步還原這一自動化的過程。
## Spring提供的事務API
Spring提供了很多關於事務的API。但是最為基本的就是`PlatformTransactionManager`、`TransactionDefintion`和`TransactionStatus`。
### 事務管理器——PlatformTransactionManager
`PlatformTransactionManager`是事務管理器的頂層介面。事務的管理是受限於具體的資料來源的(例如,JDBC對應的事務管理器就是`DatasourceTransactionManager`),因此`PlatformTransactionManager`只規定了事務的基本操作:建立事務,提交事物和回滾事務。
```java
public interface PlatformTransactionManager extends TransactionManager {
/**
* 開啟事務
*/
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
/**
* 提交事務
*/
void commit(TransactionStatus status) throws TransactionException;
/**
* 回滾事務
*/
void rollback(TransactionStatus status) throws TransactionException;
}
```
同時為了簡化事務管理器的實現,Spring提供了一個抽象類`AbstractPlatformTransactionManager`,規定了事務管理器的基本框架,僅將依賴於具體平臺的特性作為抽象方法留給子類實現。
### 事務狀態——TransactionStatus
事務狀態是我對`TransactionStatus`這個類的直譯。其實我覺得這個類可以直接當作事務的超集來看(包含了事務物件,並且儲存了事務的狀態)。`PlatformTransactionManager.getTransaction()`時建立的也正是這個物件。
這個物件的方法都和事務狀態相關:
```java
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
/**
* 是否有Savepoint Savepoint是當事務回滾時需要恢復的狀態
*/
boolean hasSavepoint();
/**
* flush()操作和底層資料來源有關,並非強制所有資料來源都要支援
*/
@Override
void flush();
}
```
此外,`TransactionStatus`還從父介面中繼承了其他方法,都歸總在下方:
```java
/**
* 是否是新事務(或是其他事務的一部分)
*/
boolean isNewTransaction();
/**
* 設定rollback-only 表示之後需要回滾
*/
void setRollbackOnly();
/**
* 是否rollback-only
*/
boolean isRollbackOnly();
/**
* 判斷該事務已經完成
*/
boolean isCompleted();
/**
* 建立一個Savepoint
*/
Object createSavepoint() throws TransactionException;
/**
* 回滾到指定Savepoint
*/
void rollbackToSavepoint(Object savepoint) throws TransactionException;
/**
* 釋放Savepoint 當事務完成後,事務管理器基本上自動釋放該事務所有的savepoint
*/
void releaseSavepoint(Object savepoint) throws TransactionException;
```
### 事務屬性的定義——TransactionDefinition
`TransactionDefinition`表示一個事務的定義,將根據它規定的特性去開啟事務。
事務的傳播等級和隔離級別的常量同樣定義在這個介面中。
```java
/**
* 返回事務的傳播級別
*/
default int getPropagationBehavior() {
return PROPAGATION_REQUIRED;
}
/**
* 返回事務的隔離級別
*/
default int getIsolationLevel() {
return ISOLATION_DEFAULT;
}
/**
* 事務超時時間
*/
default int getTimeout() {
return TIMEOUT_DEFAULT;
}
/**
* 是否為只讀事務(只讀事務在處理上能有一些優化)
*/
default boolean isReadOnly() {
return false;
}
/**
* 返回事務的名稱
*/
@Nullable
default String getName() {
return null;
}
/**
* 預設的事務配置
*/
static TransactionDefinition withDefaults() {
return StaticTransactionDefinition.INSTANCE;
}
```
### 程式設計式使用Spring事務
有了上述這些API,就已經可以通過程式設計的方式實現Spring的事務控制了。
但是Spring官方建議不要直接使用`PlatformTransactionManager`這一偏低層的API來程式設計,而是使用`TransactionTemplate`和`TransactionCallback`這兩個偏向使用者層的介面。
示例程式碼如下:
```java
//設定事務的各種屬性;可以猜測TransactionTemplate應該是實現了TransactionDefinition
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
transactionTemplate.setTimeout(30000);
//執行事務 將業務邏輯封裝在TransactionCallback中
transactionTemplate.execute(new TransactionCallback