面試中是否是有時常常會被問到 「Spring 事務如何管理的瞭解嗎?」 ,「Spring 事務的傳播性有哪些,能聊聊它們的使用場景嗎?」, 「事務回滾的時候是全部異常下都會回滾嗎?」; 下面咱們就帶着這些問題來看看 Spring 事務是如何實現的吧。java
首先咱們仍是先經過一個使用示例,先看下 Spring 事務是如何工做的。mysql
本文咱們先採用
TransactionProxyFactoryBean
配置的方式來看下, Spring 事務如何實現面試
<beans>
<!-- 配置數據源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" >
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>url</value>
</property>
<property name="username">
<value>username</value>
</property>
<property name="password">
<value>password</value>
</property>
</bean>
<!-- 配置事務管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"></ref>
</property>
</bean>
<!-- 配置 jdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<bean id="userServiceTarget" class="org.springframework.transaction.UserServiceImpl">
<property name="jdbcTemplate">
<ref bean="jdbcTemplate"></ref>
</property>
</bean>
<!-- TransactionProxyFactoryBean 實現了接口 InitializingBean,在初始化過程當中會調用 afterPropertiesSet 1 : 建立事務攔截器 2 : 建立事務 advisor (事務的攔截切面) 3 : 建立代理 -->
<bean id="userService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="target">
<ref bean="userServiceTarget"></ref>
</property>
<property name="proxyInterfaces">
<value>org.springframework.transaction.UserService</value>
</property>
<!-- 配置事務屬性 傳播行爲, 事務隔離級別, 是否只讀, 回滾規則(哪些異常下執行回滾),key 爲配置須要事務管理的方法名; 在代理目標執行的時候會經過該屬性判斷方法是否須要事務管理 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
複製代碼
在 TransactionProxyFactoryBean
的屬性配置中若是您對 transactionAttributes
屬性不熟悉的話,是否是會感受一頭霧水呢? 這個玩意怎麼配置的? 配置格式又是什麼樣的呢? 配置值有哪些呢 ?; 下面將會經過對 TransactionProxyFactoryBean
的源碼分析來一一解答。spring
從 TransactionProxyFactoryBean
類結構咱們知道,其實現了接口 InitializingBean
和 FactoryBean
; 那麼也就是在 TransactionProxyFactoryBean
實例化後會調用方法 afterPropertiesSet
, 在獲取目標對象實例時會調用方法 getObject
; 下面將主要看下這兩個方法的實現。sql
public void afterPropertiesSet() throws AopConfigException {
// 校驗 Target 目標對象
if (this.target == null) {
throw new AopConfigException("Target must be set");
}
// 校驗事務屬性定義,從拋出的異常信息能夠看出 Spring 在此作了強校驗;
// 也就是說若是沒有須要 Spring 事務管理的方法,就不要採用 TransactionProxyFactoryBean 了
// 那麼 transactionAttributeSource 是怎麼來的呢? 見下文分析
if (this.transactionAttributeSource == null) {
throw new AopConfigException("Either 'transactionAttributeSource' or 'transactionAttributes' is required: " +
"If there are no transactional methods, don't use a transactional proxy.");
}
// 建立事務攔截器 transactionInterceptor
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
transactionInterceptor.setTransactionManager(this.transactionManager);
transactionInterceptor.setTransactionAttributeSource(this.transactionAttributeSource);
transactionInterceptor.afterPropertiesSet();
ProxyFactory proxyFactory = new ProxyFactory();
// 是否配置了前置攔截
if (this.preInterceptors != null) {
for (int i = 0; i < this.preInterceptors.length; i++) {
proxyFactory.addAdvisor(GlobalAdvisorAdapterRegistry.getInstance().wrap(this.preInterceptors[i]));
}
}
if (this.pointcut != null) {
// 若是配置了 pointcut 切入點,則按配置的 pointcut 建立 advisor
Advisor advice = new DefaultPointcutAdvisor(this.pointcut, transactionInterceptor);
proxyFactory.addAdvisor(advice);
}
else {
// rely on default pointcut
// 建立事務攔截切面 advisor
proxyFactory.addAdvisor(new TransactionAttributeSourceAdvisor(transactionInterceptor));
// could just do the following, but it's usually less efficient because of AOP advice chain caching
// proxyFactory.addInterceptor(transactionInterceptor);
}
// 是否配置了後置攔截
if (this.postInterceptors != null) {
for (int i = 0; i < this.postInterceptors.length; i++) {
proxyFactory.addAdvisor(GlobalAdvisorAdapterRegistry.getInstance().wrap(this.postInterceptors[i]));
}
}
proxyFactory.copyFrom(this);
proxyFactory.setTargetSource(createTargetSource(this.target));
// 設置代理的接口
if (this.proxyInterfaces != null) {
proxyFactory.setInterfaces(this.proxyInterfaces);
}
else if (!getProxyTargetClass()) {
// rely on AOP infrastructure to tell us what interfaces to proxy
proxyFactory.setInterfaces(AopUtils.getAllInterfaces(this.target));
}
// 建立目標對象的代理對象
this.proxy = proxyFactory.getProxy();
}
複製代碼
從源碼中咱們知道 afterPropertiesSet
主要作如下幾件事:數據庫
在
afterPropertiesSet
的實現中有個針對transactionAttributeSource
的非空校驗,那麼這個變量是什麼時候賦值的呢 ? 還記得使用示例中的關於事務屬性的定義transactionAttributes
嗎 ?apache
public void setTransactionAttributes(Properties transactionAttributes) {
NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
tas.setProperties(transactionAttributes);
this.transactionAttributeSource = tas;
}
public void setProperties(Properties transactionAttributes) {
TransactionAttributeEditor tae = new TransactionAttributeEditor();
// 遍歷 properties
for (Iterator it = transactionAttributes.keySet().iterator(); it.hasNext(); ) {
// key 爲匹配的方法名
String methodName = (String) it.next();
String value = transactionAttributes.getProperty(methodName);
// 解析 value
tae.setAsText(value);
TransactionAttribute attr = (TransactionAttribute) tae.getValue();
// 將方法名與事務屬性定義匹配關聯
addTransactionalMethod(methodName, attr);
}
}
複製代碼
下面咱們就看下 setAsText
方法是如何解析事務屬性的配置app
/** * Format is PROPAGATION_NAME,ISOLATION_NAME,readOnly,+Exception1,-Exception2. * Null or the empty string means that the method is non transactional. * @see java.beans.PropertyEditor#setAsText(java.lang.String) */
public void setAsText(String s) throws IllegalArgumentException {
if (s == null || "".equals(s)) {
setValue(null);
}
else {
// tokenize it with ","
// 按 , 分割配置信息
String[] tokens = StringUtils.commaDelimitedListToStringArray(s);
RuleBasedTransactionAttribute attr = new RuleBasedTransactionAttribute();
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i];
// 以 PROPAGATION 開頭,則配置事務傳播性
if (token.startsWith(TransactionDefinition.PROPAGATION_CONSTANT_PREFIX)) {
attr.setPropagationBehaviorName(tokens[i]);
}
// 以 ISOLATION 開頭,則配置事務隔離級別
else if (token.startsWith(TransactionDefinition.ISOLATION_CONSTANT_PREFIX)) {
attr.setIsolationLevelName(tokens[i]);
}
// 以 timeout_ 開頭,則設置事務超時時間
else if (token.startsWith(DefaultTransactionAttribute.TIMEOUT_PREFIX)) {
String value = token.substring(DefaultTransactionAttribute.TIMEOUT_PREFIX.length());
attr.setTimeout(Integer.parseInt(value));
}
// 若等於 readOnly 則配置事務只讀
else if (token.equals(DefaultTransactionAttribute.READ_ONLY_MARKER)) {
attr.setReadOnly(true);
}
// 以 + 開頭,則配置哪些異常下不回滾
else if (token.startsWith(DefaultTransactionAttribute.COMMIT_RULE_PREFIX)) {
attr.getRollbackRules().add(new NoRollbackRuleAttribute(token.substring(1)));
}
// 以 - 開頭,則配置哪些異常下回滾
else if (token.startsWith(DefaultTransactionAttribute.ROLLBACK_RULE_PREFIX)) {
attr.getRollbackRules().add(new RollbackRuleAttribute(token.substring(1)));
}
else {
throw new IllegalArgumentException("Illegal transaction token: " + token);
}
}
setValue(attr);
}
}
複製代碼
從 setAsText
方法的實現咱們就能夠搞明白在配置文件中 transactionAttributes
如何配置了,譬如:less
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED, ISOLATION_DEFAULT, readOnly</prop>
</props>
</property>
複製代碼
也能夠這樣配置:工具
<property name="transactionAttributes">
<props>
<prop key="*">readOnly, ISOLATION_DEFAULT, PROPAGATION_REQUIRED</prop>
</props>
</property>
複製代碼
也就是說 transactionAttributes
的配置只要保證 token
格式正確便可,順序無關;可是從規範來說建議仍是保持 PROPAGATION_NAME,ISOLATION_NAME,readOnly,+Exception1,-Exception2.
的格式。
public Object getObject() {
// proxy 對象在 afterPropertiesSet 方法執行時產生
return this.proxy;
}
複製代碼
在 重拾-Spring AOP
中咱們知道,當代理對象在執行的時候會先獲取當前方法所匹配的 advisor
(參見類 JdkDynamicAopProxy
); 而 TransactionProxyFactoryBean
在建立代理對象的時候會將 TransactionInterceptor
綁定到 TransactionAttributeSourceAdvisor
上,那麼我就看下 TransactionAttributeSourceAdvisor
是如何匹配方法的。
public class TransactionAttributeSourceAdvisor extends StaticMethodMatcherPointcutAdvisor {
private TransactionAttributeSource transactionAttributeSource;
public TransactionAttributeSourceAdvisor(TransactionInterceptor ti) {
super(ti);
if (ti.getTransactionAttributeSource() == null) {
throw new AopConfigException("Cannot construct a TransactionAttributeSourceAdvisor using a " +
"TransactionInterceptor that has no TransactionAttributeSource configured");
}
this.transactionAttributeSource = ti.getTransactionAttributeSource();
}
public boolean matches(Method m, Class targetClass) {
return (this.transactionAttributeSource.getTransactionAttribute(m, targetClass) != null);
}
}
複製代碼
TransactionAttributeSourceAdvisor
判斷方法是否匹配時,實際是由 NameMatchTransactionAttributeSource
的方法 getTransactionAttribute
來處理。
public TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {
// 獲取目標方法名
String methodName = method.getName();
// 獲取目標方法匹配的事務屬性定義
TransactionAttribute attr = (TransactionAttribute) this.nameMap.get(methodName);
// 若是 attr 不爲空說明當前方法配置了事務屬性定義,也就是當前方法須要事務管理
if (attr != null) {
return attr;
}
else {
// look up most specific name match
String bestNameMatch = null;
for (Iterator it = this.nameMap.keySet().iterator(); it.hasNext();) {
// 判斷當前方法是否匹配通配符的方式
String mappedName = (String) it.next();
if (isMatch(methodName, mappedName) &&
(bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
attr = (TransactionAttribute) this.nameMap.get(mappedName);
bestNameMatch = mappedName;
}
}
return attr;
}
}
protected boolean isMatch(String methodName, String mappedName) {
return (mappedName.endsWith("*") && methodName.startsWith(mappedName.substring(0, mappedName.length() - 1))) ||
(mappedName.startsWith("*") && methodName.endsWith(mappedName.substring(1, mappedName.length())));
}
複製代碼
在完成判斷當前方法是否須要事務管理後,若是須要事務管理最終會調用 TransactionInterceptor
執行事務攔截的處理。
public final Object invoke(MethodInvocation invocation) throws Throwable {
Class targetClass = (invocation.getThis() != null) ? invocation.getThis().getClass() : null;
// if the transaction attribute is null, the method is non-transactional
// 獲取當前方法所支持的事務配置屬性,若不存在則說明當前方法不須要事務管理
TransactionAttribute transAtt = this.transactionAttributeSource.getTransactionAttribute(invocation.getMethod(), targetClass);
TransactionStatus status = null;
TransactionStatus oldTransactionStatus = null;
// create transaction if necessary
if (transAtt != null) {
// the transaction manager will flag an error if an incompatible tx already exists
// 經過事務管理獲取事務,該事務多是新建立的也多是當前上下文已存在的事務
// 返回事務狀態
status = this.transactionManager.getTransaction(transAtt);
// make the TransactionStatus available to callees
oldTransactionStatus = (TransactionStatus) currentTransactionStatus.get();
currentTransactionStatus.set(status);
}
else {
// it isn't a transactional method
}
Object retVal = null;
try {
// 目標方法執行
retVal = invocation.proceed();
}
catch (Throwable ex) {
// target invocation exception
if (status != null) {
// 異常處理 可能會執行事務的回滾
onThrowable(invocation, transAtt, status, ex);
}
throw ex;
}
finally {
if (transAtt != null) {
// use stack to restore old transaction status if one was set
currentTransactionStatus.set(oldTransactionStatus);
}
}
if (status != null) {
// 經過事務管理執行事務提交
this.transactionManager.commit(status);
}
return retVal;
}
複製代碼
private void onThrowable(MethodInvocation invocation, TransactionAttribute txAtt, TransactionStatus status, Throwable ex) {
// 判斷異常是否須要回滾
if (txAtt.rollbackOn(ex)) {
try {
// 經過事務管理執行回滾
this.transactionManager.rollback(status);
}
catch (TransactionException tex) {
logger.error("Application exception overridden by rollback exception", ex);
throw tex;
}
}
else {
// Will still roll back if rollbackOnly is true
// 異常不須要回滾的話 則提交事務
this.transactionManager.commit(status);
}
}
複製代碼
從 TransactionInterceptor
的處理邏輯來看,咱們知道其主要作如下事情:
rollback
,反之執行事務 commit
commit
也就是說
TransactionInterceptor
(事務攔截器) 主要是將事務相關的動做委託給TransactionManager
(事務管理器)處理
本文是以
DataSourceTransactionManager
爲例來分析事務的管理實現
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
// 獲取事務
Object transaction = doGetTransaction();
if (definition == null) {
// 若 definition == null 則採用默認的事務定義
definition = new DefaultTransactionDefinition();
}
// 判斷當前上下文是否開啓過事務
if (isExistingTransaction(transaction)) {
// 當前上下文開啓過事務
// 若是當前方法匹配的事務傳播性爲 PROPAGATION_NEVER 說明不須要事務則拋出異常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException("Transaction propagation 'never' but existing transaction found");
}
// 若是當前方法匹配的事務傳播性爲 PROPAGATION_NOT_SUPPORTED 說明該方法不該該運行在事務中,則將當前事務掛起
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
// 將當前事務掛起
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (this.transactionSynchronization == SYNCHRONIZATION_ALWAYS);
// 返回的事務狀態爲 不須要事務
return newTransactionStatus(null, false, newSynchronization,
definition.isReadOnly(), debugEnabled, suspendedResources);
}
// 若是當前方法匹配的事務傳播性爲 PROPAGATION_REQUIRES_NEW 表示當前方法必須運行在它本身的事務中;將已存在的事務掛起,從新開啓事務
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Creating new transaction, suspending current one");
}
// 掛起當前事務
Object suspendedResources = suspend(transaction);
// 從新開啓個事務
doBegin(transaction, definition);
boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
// 返回的事務狀態爲 新建事務
return newTransactionStatus(transaction, true, newSynchronization,
definition.isReadOnly(), debugEnabled, suspendedResources);
}
else {
boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
// 其餘的傳播行爲 表示在已存在的事務中執行
return newTransactionStatus(transaction, false, newSynchronization,
definition.isReadOnly(), debugEnabled, null);
}
}
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// 若是傳播性爲 PROPAGATION_MANDATORY 說明必須在事務中執行,若當前沒有事務的話則拋出異常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException("Transaction propagation 'mandatory' but no existing transaction found");
}
// 當前上下文不存在事務
// 若傳播性爲 PROPAGATION_REQUIRED 或 PROPAGATION_REQUIRES_NEW 則開啓新的事務執行
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
// 開啓新的 connection 並取消自動提交,將 connection 綁定當前線程
doBegin(transaction, definition);
boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
return newTransactionStatus(transaction, true, newSynchronization,
definition.isReadOnly(), debugEnabled, null);
}
else {
// "empty" (-> no) transaction
boolean newSynchronization = (this.transactionSynchronization == SYNCHRONIZATION_ALWAYS);
// 返回事務狀態爲 不須要事務
return newTransactionStatus(null, false, newSynchronization,
definition.isReadOnly(), debugEnabled, null);
}
}
protected Object doGetTransaction() {
// 判斷當前線程是否開啓過事務
if (TransactionSynchronizationManager.hasResource(this.dataSource)) {
// 獲取當前已存在的 connectoin holder
ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
return new DataSourceTransactionObject(holder);
}
else {
return new DataSourceTransactionObject();
}
}
複製代碼
看到了這裏,是否是忽然明白 PROPAGATION
(事務傳播性) 是幹什麼的了;
簡單來講,
PROPAGATION
就是爲了告訴 Spring 當前方法需不須要事務,是在已存在的事務中執行,仍是新開啓事務執行;也能夠認爲是繼承上個方法棧的事務,仍是擁有本身的事務。
TransactionManager
獲取事務的過程實際就是經過當前方法定義的 PROPAGATION
(事務傳播性) 和當前上下文是否存在事務來判斷是否須要事務,是否須要開啓新的事務或者是使用當前已存在的事務。
下面看下如何開啓新的事務 doBegin
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// cache to avoid repeated checks
boolean debugEnabled = logger.isDebugEnabled();
// 判斷 connection holder 是否爲空
// 兩種場景下可能爲空:
// 1. 上下文不存在事務的時候
// 2. 上下文已存在的事務被掛起的時候
if (txObject.getConnectionHolder() == null) {
if (debugEnabled) {
logger.debug("Opening new connection for JDBC transaction");
}
// 開啓新的 connection
Connection con = DataSourceUtils.getConnection(this.dataSource, false);
txObject.setConnectionHolder(new ConnectionHolder(con));
}
Connection con = txObject.getConnectionHolder().getConnection();
try {
// apply read-only
if (definition.isReadOnly()) {
try {
// 若是定義了只讀,設置 connection 爲只讀
con.setReadOnly(true);
}
catch (Exception ex) {
// SQLException or UnsupportedOperationException
logger.warn("Could not set JDBC connection read-only", ex);
}
}
// apply isolation level
// 設置事務隔離級別
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
txObject.setPreviousIsolationLevel(new Integer(con.getTransactionIsolation()));
con.setTransactionIsolation(definition.getIsolationLevel());
}
// 若 connection 爲自動提交則取消
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
con.setAutoCommit(false);
}
// 設置超時時間
if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(definition.getTimeout());
}
// 將當前 connection holder 綁定到當前上下文
TransactionSynchronizationManager.bindResource(this.dataSource, txObject.getConnectionHolder());
}
catch (SQLException ex) {
throw new CannotCreateTransactionException("Could not configure connection", ex);
}
}
複製代碼
doBegin
執行開啓事務的操做,在上下文不存在事務或者上下文事務被掛起的時候會新打開一個 connection
, 並按照事務定義設置相關屬性,譬如是否只讀,取消自動提交,設置事務隔離級別,設置超時時間;最後會將 connection
綁定到當前上下文,也即當前線程。
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 將當前事務的 connection holder 置爲空
txObject.setConnectionHolder(null);
// 並將當前事務與上下文解綁
return TransactionSynchronizationManager.unbindResource(this.dataSource);
}
複製代碼
事務掛起既是將當前事務的鏈接持有者清空並與當前上下文解綁,保證後續可以從新開啓事務。
針對數據庫的操做,本文以 Spring 提供的
jdbcTemplate
工具類進行分析。
public Object execute(final StatementCallback action) {
// 若當前須要事務管理的話,那麼此時獲取的 connection 則是 transaction manager bind 的 connection
// 這樣就保證數據庫操做的時候所得到的的 connection 與 事務管理的一致
Connection con = DataSourceUtils.getConnection(getDataSource());
Statement stmt = null;
// 如下代碼省略 此處重點關注如何獲取 connection
}
複製代碼
public static Connection getConnection(DataSource ds, boolean allowSynchronization) throws CannotGetJdbcConnectionException {
// 從當前上下文獲取 connection holder
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(ds);
if (conHolder != null) {
return conHolder.getConnection();
}
else {
try {
// 反之新打開一個 connection
Connection con = ds.getConnection();
if (allowSynchronization && TransactionSynchronizationManager.isSynchronizationActive()) {
logger.debug("Registering transaction synchronization for JDBC connection");
// use same Connection for further JDBC actions within the transaction
// thread object will get removed by synchronization at transaction completion
conHolder = new ConnectionHolder(con);
TransactionSynchronizationManager.bindResource(ds, conHolder);
TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(conHolder, ds));
}
return con;
}
catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Could not get JDBC connection", ex);
}
}
}
複製代碼
從上述代碼咱們能夠看到,當經過 jdbcTemplate
操做數據庫時會先從當前上下文中獲取 connection
; 這樣就保證了所獲取的事務與事務攔截器的事務爲同一個實例,也就是將事務交給了 Spring 來管理。
public final void commit(TransactionStatus status) throws TransactionException {
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 省略
else {
try {
try {
triggerBeforeCommit(defStatus);
triggerBeforeCompletion(defStatus);
if (status.isNewTransaction()) {
logger.info("Initiating transaction commit");
// 執行事務提交
doCommit(defStatus);
}
}
// 省略
}
finally {
cleanupAfterCompletion(defStatus);
}
}
}
複製代碼
doCommit
執行事務提交
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction [" + txObject.getConnectionHolder().getConnection() + "]");
}
try {
// 事務提交
txObject.getConnectionHolder().getConnection().commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit", ex);
}
}
複製代碼
從上文的 commit
事務提交操做發現,在完成事務提交以後,還有個後置動做 cleanupAfterCompletion
, 該方法會對掛起中的事務執行恢復操做。
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clearSynchronization();
}
if (status.isNewTransaction()) {
doCleanupAfterCompletion(status.getTransaction());
}
// 當存在掛起的事務時,執行恢復掛起的事務
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction");
}
resume(status.getTransaction(), status.getSuspendedResources());
}
}
複製代碼
protected void doResume(Object transaction, Object suspendedResources) {
// 將掛起的事務綁定的 connection 從新綁定到當前上下文
ConnectionHolder conHolder = (ConnectionHolder) suspendedResources;
TransactionSynchronizationManager.bindResource(this.dataSource, conHolder);
}
複製代碼
事務的 resume
就是將掛起的事務從新綁定到當前上下文中。
當 TransactionInterceptor
調用目標方法執行出現異常的時候,會進行異常處理執行方法 onThrowable
private void onThrowable(MethodInvocation invocation, TransactionAttribute txAtt, TransactionStatus status, Throwable ex) {
if (txAtt.rollbackOn(ex)) {
try {
// 異常須要回滾
this.transactionManager.rollback(status);
}
catch (TransactionException tex) {
throw tex;
}
}
else {
// 異常不須要回滾的話 則提交事務
this.transactionManager.commit(status);
}
}
複製代碼
onThrowable
方法會經過配置判斷當前異常是否須要回滾。
public final void rollback(TransactionStatus status) throws TransactionException {
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
try {
try {
triggerBeforeCompletion(defStatus);
if (status.isNewTransaction()) {
// 執行事務回滾
logger.info("Initiating transaction rollback");
doRollback(defStatus);
}
else if (defStatus.getTransaction() != null) {
if (defStatus.isDebug()) {
logger.debug("Setting existing transaction rollback-only");
}
doSetRollbackOnly(defStatus);
}
else {
logger.info("Should roll back transaction but cannot - no transaction available");
}
}
catch (TransactionException ex) {
triggerAfterCompletion(defStatus, TransactionSynchronization.STATUS_UNKNOWN, ex);
throw ex;
}
triggerAfterCompletion(defStatus, TransactionSynchronization.STATUS_ROLLED_BACK, null);
}
finally {
cleanupAfterCompletion(defStatus);
}
}
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
try {
// 執行回滾
txObject.getConnectionHolder().getConnection().rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not rollback", ex);
}
}
複製代碼
此時咱們基本明白了 Spring Transaction 的實現原理,下面對其實現作個小結:
TransactionInterceptor
攔截TransactionInterceptor
會委派 TransactionManager
執行事務的建立,事務提交,事務回滾的動做TransactionManager
會根據當前方法配置的事務傳播性及當前上下文是否存在事務來判斷是否新建事務TransactionManager
當新建事務時會將事務綁定到當前上下文,以保證目標方法執行時獲取的事務爲同一實例TransactionManager
執行事務掛起時會將當前事務與當前上下文解除綁定關係TransactionManager
執行事務恢復時會將已掛起的事務從新與當前上下文綁定