摘要:當存在掛起的事務(wù)時,執(zhí)行恢復(fù)掛起的事務(wù)將掛起的事務(wù)綁定的重新綁定到當前上下文事務(wù)的就是將掛起的事務(wù)重新綁定到當前上下文中。
問題
面試中是不是有時經(jīng)常會被問到 “Spring 事務(wù)如何管理的了解嗎?” ,“Spring 事務(wù)的傳播性有哪些,能聊聊它們的使用場景嗎?”, “事務(wù)回滾的時候是所有異常下都會回滾嗎?”; 下面我們就帶著這些問題來看看 Spring 事務(wù)是如何實現(xiàn)的吧。
實現(xiàn)分析首先我們還是先通過一個使用示例,先看下 Spring 事務(wù)是如何工作的。
使用示例本文我們先采用 TransactionProxyFactoryBean 配置的方式來看下, Spring 事務(wù)如何實現(xiàn)
com.mysql.jdbc.Driver url username password org.springframework.transaction.UserService PROPAGATION_REQUIRED
在 TransactionProxyFactoryBean 的屬性配置中如果您對 transactionAttributes 屬性不熟悉的話,是不是會感覺一頭霧水呢? 這個玩意怎么配置的? 配置格式又是什么樣的呢? 配置值有哪些呢 ?; 下面將會通過對 TransactionProxyFactoryBean 的源碼分析來一一解答。
源碼分析 類結(jié)構(gòu)從 TransactionProxyFactoryBean 類結(jié)構(gòu)我們知道,其實現(xiàn)了接口 InitializingBean 和 FactoryBean; 那么也就是在 TransactionProxyFactoryBean 實例化后會調(diào)用方法 afterPropertiesSet, 在獲取目標對象實例時會調(diào)用方法 getObject; 下面將主要看下這兩個方法的實現(xiàn)。
afterPropertiesSet-創(chuàng)建目標代理對象public void afterPropertiesSet() throws AopConfigException { // 校驗 Target 目標對象 if (this.target == null) { throw new AopConfigException("Target must be set"); } // 校驗事務(wù)屬性定義,從拋出的異常信息可以看出 Spring 在此做了強校驗; // 也就是說如果沒有需要 Spring 事務(wù)管理的方法,就不要采用 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."); } // 創(chuàng)建事務(wù)攔截器 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 創(chuàng)建 advisor Advisor advice = new DefaultPointcutAdvisor(this.pointcut, transactionInterceptor); proxyFactory.addAdvisor(advice); } else { // rely on default pointcut // 創(chuàng)建事務(wù)攔截切面 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)); // 設(shè)置代理的接口 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)); } // 創(chuàng)建目標對象的代理對象 this.proxy = proxyFactory.getProxy(); }
從源碼中我們知道 afterPropertiesSet 主要做以下幾件事:
參數(shù)有效性校驗; 校驗?zāi)繕藢ο?,事?wù)屬性定義
設(shè)置代理的 advisor chain, 包括用戶自定義的前置攔截, 內(nèi)置的事務(wù)攔截器,用戶自定義的后置攔截
創(chuàng)建目標代理對象
在 afterPropertiesSet 的實現(xiàn)中有個針對 transactionAttributeSource 的非空校驗,那么這個變量是何時賦值的呢 ? 還記得使用示例中的關(guān)于事務(wù)屬性的定義 transactionAttributes 嗎 ?setTransactionAttributes-設(shè)置事務(wù)屬性定義
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(); // 將方法名與事務(wù)屬性定義匹配關(guān)聯(lián) addTransactionalMethod(methodName, attr); } }
下面我們就看下 setAsText 方法是如何解析事務(wù)屬性的配置
/** * 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 開頭,則配置事務(wù)傳播性 if (token.startsWith(TransactionDefinition.PROPAGATION_CONSTANT_PREFIX)) { attr.setPropagationBehaviorName(tokens[i]); } // 以 ISOLATION 開頭,則配置事務(wù)隔離級別 else if (token.startsWith(TransactionDefinition.ISOLATION_CONSTANT_PREFIX)) { attr.setIsolationLevelName(tokens[i]); } // 以 timeout_ 開頭,則設(shè)置事務(wù)超時時間 else if (token.startsWith(DefaultTransactionAttribute.TIMEOUT_PREFIX)) { String value = token.substring(DefaultTransactionAttribute.TIMEOUT_PREFIX.length()); attr.setTimeout(Integer.parseInt(value)); } // 若等于 readOnly 則配置事務(wù)只讀 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 方法的實現(xiàn)我們就可以搞明白在配置文件中 transactionAttributes 如何配置了,譬如:
PROPAGATION_REQUIRED, ISOLATION_DEFAULT, readOnly
也可以這樣配置:
readOnly, ISOLATION_DEFAULT, PROPAGATION_REQUIRED
也就是說 transactionAttributes 的配置只要保證 token 格式正確即可,順序無關(guān);但是從規(guī)范來講建議還是保持 PROPAGATION_NAME,ISOLATION_NAME,readOnly,+Exception1,-Exception2. 的格式。
getObject-獲取代理對象public Object getObject() { // proxy 對象在 afterPropertiesSet 方法執(zhí)行時產(chǎn)生 return this.proxy; }代理執(zhí)行
在 重拾-Spring AOP 中我們知道,當代理對象在執(zhí)行的時候會先獲取當前方法所匹配的 advisor (參見類 JdkDynamicAopProxy); 而 TransactionProxyFactoryBean 在創(chuàng)建代理對象的時候會將 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(); // 獲取目標方法匹配的事務(wù)屬性定義 TransactionAttribute attr = (TransactionAttribute) this.nameMap.get(methodName); // 如果 attr 不為空說明當前方法配置了事務(wù)屬性定義,也就是當前方法需要事務(wù)管理 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()))); }
在完成判斷當前方法是否需要事務(wù)管理后,如果需要事務(wù)管理最終會調(diào)用 TransactionInterceptor 執(zhí)行事務(wù)攔截的處理。
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 // 獲取當前方法所支持的事務(wù)配置屬性,若不存在則說明當前方法不需要事務(wù)管理 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 // 通過事務(wù)管理獲取事務(wù),該事務(wù)可能是新創(chuàng)建的也可能是當前上下文已存在的事務(wù) // 返回事務(wù)狀態(tài) 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 { // 目標方法執(zhí)行 retVal = invocation.proceed(); } catch (Throwable ex) { // target invocation exception if (status != null) { // 異常處理 可能會執(zhí)行事務(wù)的回滾 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) { // 通過事務(wù)管理執(zhí)行事務(wù)提交 this.transactionManager.commit(status); } return retVal; }
private void onThrowable(MethodInvocation invocation, TransactionAttribute txAtt, TransactionStatus status, Throwable ex) { // 判斷異常是否需要回滾 if (txAtt.rollbackOn(ex)) { try { // 通過事務(wù)管理執(zhí)行回滾 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 // 異常不需要回滾的話 則提交事務(wù) this.transactionManager.commit(status); } }
從 TransactionInterceptor 的處理邏輯來看,我們知道其主要做以下事情:
獲取當前方法所定義的事務(wù)屬性
通過事務(wù)管理器 Transaction Manager 來獲取事務(wù)
目標方法執(zhí)行
執(zhí)行異常處理,如異常需要回滾則通過事務(wù)管理器執(zhí)行事務(wù) rollback,反之執(zhí)行事務(wù) commit
方法執(zhí)行成功則執(zhí)行事務(wù) commit
也就是說 TransactionInterceptor (事務(wù)攔截器) 主要是將事務(wù)相關(guān)的動作委托給 TransactionManager (事務(wù)管理器)處理
本文是以 DataSourceTransactionManager 為例來分析事務(wù)的管理實現(xiàn)
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { // 獲取事務(wù) Object transaction = doGetTransaction(); if (definition == null) { // 若 definition == null 則采用默認的事務(wù)定義 definition = new DefaultTransactionDefinition(); } // 判斷當前上下文是否開啟過事務(wù) if (isExistingTransaction(transaction)) { // 當前上下文開啟過事務(wù) // 如果當前方法匹配的事務(wù)傳播性為 PROPAGATION_NEVER 說明不需要事務(wù)則拋出異常 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) { throw new IllegalTransactionStateException("Transaction propagation "never" but existing transaction found"); } // 如果當前方法匹配的事務(wù)傳播性為 PROPAGATION_NOT_SUPPORTED 說明該方法不應(yīng)該運行在事務(wù)中,則將當前事務(wù)掛起 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) { // 將當前事務(wù)掛起 Object suspendedResources = suspend(transaction); boolean newSynchronization = (this.transactionSynchronization == SYNCHRONIZATION_ALWAYS); // 返回的事務(wù)狀態(tài)為 不需要事務(wù) return newTransactionStatus(null, false, newSynchronization, definition.isReadOnly(), debugEnabled, suspendedResources); } // 如果當前方法匹配的事務(wù)傳播性為 PROPAGATION_REQUIRES_NEW 表示當前方法必須運行在它自己的事務(wù)中;將已存在的事務(wù)掛起,重新開啟事務(wù) if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) { if (debugEnabled) { logger.debug("Creating new transaction, suspending current one"); } // 掛起當前事務(wù) Object suspendedResources = suspend(transaction); // 重新開啟個事務(wù) doBegin(transaction, definition); boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER); // 返回的事務(wù)狀態(tài)為 新建事務(wù) return newTransactionStatus(transaction, true, newSynchronization, definition.isReadOnly(), debugEnabled, suspendedResources); } else { boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER); // 其他的傳播行為 表示在已存在的事務(wù)中執(zhí)行 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 說明必須在事務(wù)中執(zhí)行,若當前沒有事務(wù)的話則拋出異常 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException("Transaction propagation "mandatory" but no existing transaction found"); } // 當前上下文不存在事務(wù) // 若傳播性為 PROPAGATION_REQUIRED 或 PROPAGATION_REQUIRES_NEW 則開啟新的事務(wù)執(zhí)行 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); // 返回事務(wù)狀態(tài)為 不需要事務(wù) return newTransactionStatus(null, false, newSynchronization, definition.isReadOnly(), debugEnabled, null); } } protected Object doGetTransaction() { // 判斷當前線程是否開啟過事務(wù) if (TransactionSynchronizationManager.hasResource(this.dataSource)) { // 獲取當前已存在的 connectoin holder ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource); return new DataSourceTransactionObject(holder); } else { return new DataSourceTransactionObject(); } }
看到了這里,是不是突然明白 PROPAGATION (事務(wù)傳播性) 是干什么的了;
簡單來說, PROPAGATION 就是為了告訴 Spring 當前方法需不需要事務(wù),是在已存在的事務(wù)中執(zhí)行,還是新開啟事務(wù)執(zhí)行;也可以認為是繼承上個方法棧的事務(wù),還是擁有自己的事務(wù)。
TransactionManager 獲取事務(wù)的過程實際就是通過當前方法定義的 PROPAGATION (事務(wù)傳播性) 和當前上下文是否存在事務(wù)來判斷是否需要事務(wù),是否需要開啟新的事務(wù)或者是使用當前已存在的事務(wù)。
下面看下如何開啟新的事務(wù) doBegin
protected void doBegin(Object transaction, TransactionDefinition definition) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; // cache to avoid repeated checks boolean debugEnabled = logger.isDebugEnabled(); // 判斷 connection holder 是否為空 // 兩種場景下可能為空: // 1. 上下文不存在事務(wù)的時候 // 2. 上下文已存在的事務(wù)被掛起的時候 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 { // 如果定義了只讀,設(shè)置 connection 為只讀 con.setReadOnly(true); } catch (Exception ex) { // SQLException or UnsupportedOperationException logger.warn("Could not set JDBC connection read-only", ex); } } // apply isolation level // 設(shè)置事務(wù)隔離級別 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); } // 設(shè)置超時時間 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 執(zhí)行開啟事務(wù)的操作,在上下文不存在事務(wù)或者上下文事務(wù)被掛起的時候會新打開一個 connection, 并按照事務(wù)定義設(shè)置相關(guān)屬性,譬如是否只讀,取消自動提交,設(shè)置事務(wù)隔離級別,設(shè)置超時時間;最后會將 connection 綁定到當前上下文,也即當前線程。
protected Object doSuspend(Object transaction) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; // 將當前事務(wù)的 connection holder 置為空 txObject.setConnectionHolder(null); // 并將當前事務(wù)與上下文解綁 return TransactionSynchronizationManager.unbindResource(this.dataSource); }
事務(wù)掛起既是將當前事務(wù)的連接持有者清空并與當前上下文解綁,保證后續(xù)能夠重新開啟事務(wù)。
針對數(shù)據(jù)庫的操作,本文以 Spring 提供的 jdbcTemplate 工具類進行分析。
public Object execute(final StatementCallback action) { // 若當前需要事務(wù)管理的話,那么此時獲取的 connection 則是 transaction manager bind 的 connection // 這樣就保證數(shù)據(jù)庫操作的時候所獲得的的 connection 與 事務(wù)管理的一致 Connection con = DataSourceUtils.getConnection(getDataSource()); Statement stmt = null; // 以下代碼省略 此處重點關(guān)注如何獲取 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 操作數(shù)據(jù)庫時會先從當前上下文中獲取 connection; 這樣就保證了所獲取的事務(wù)與事務(wù)攔截器的事務(wù)為同一個實例,也就是將事務(wù)交給了 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"); // 執(zhí)行事務(wù)提交 doCommit(defStatus); } } // 省略 } finally { cleanupAfterCompletion(defStatus); } } }
doCommit 執(zhí)行事務(wù)提交
protected void doCommit(DefaultTransactionStatus status) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); if (status.isDebug()) { logger.debug("Committing JDBC transaction [" + txObject.getConnectionHolder().getConnection() + "]"); } try { // 事務(wù)提交 txObject.getConnectionHolder().getConnection().commit(); } catch (SQLException ex) { throw new TransactionSystemException("Could not commit", ex); } }
從上文的 commit 事務(wù)提交操作發(fā)現(xiàn),在完成事務(wù)提交之后,還有個后置動作 cleanupAfterCompletion, 該方法會對掛起中的事務(wù)執(zhí)行恢復(fù)操作。
private void cleanupAfterCompletion(DefaultTransactionStatus status) { if (status.isNewSynchronization()) { TransactionSynchronizationManager.clearSynchronization(); } if (status.isNewTransaction()) { doCleanupAfterCompletion(status.getTransaction()); } // 當存在掛起的事務(wù)時,執(zhí)行恢復(fù)掛起的事務(wù) if (status.getSuspendedResources() != null) { if (status.isDebug()) { logger.debug("Resuming suspended transaction"); } resume(status.getTransaction(), status.getSuspendedResources()); } }
protected void doResume(Object transaction, Object suspendedResources) { // 將掛起的事務(wù)綁定的 connection 重新綁定到當前上下文 ConnectionHolder conHolder = (ConnectionHolder) suspendedResources; TransactionSynchronizationManager.bindResource(this.dataSource, conHolder); }
事務(wù)的 resume 就是將掛起的事務(wù)重新綁定到當前上下文中。
當 TransactionInterceptor 調(diào)用目標方法執(zhí)行出現(xiàn)異常的時候,會進行異常處理執(zhí)行方法 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 { // 異常不需要回滾的話 則提交事務(wù) this.transactionManager.commit(status); } }
onThrowable 方法會通過配置判斷當前異常是否需要回滾。
public final void rollback(TransactionStatus status) throws TransactionException { DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; try { try { triggerBeforeCompletion(defStatus); if (status.isNewTransaction()) { // 執(zhí)行事務(wù)回滾 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 { // 執(zhí)行回滾 txObject.getConnectionHolder().getConnection().rollback(); } catch (SQLException ex) { throw new TransactionSystemException("Could not rollback", ex); } }小結(jié)
此時我們基本明白了 Spring Transaction 的實現(xiàn)原理,下面對其實現(xiàn)做個小結(jié):
Spring Transaction 是基于 Spring AOP 的一種實現(xiàn)
Spring Transaction 通過配置創(chuàng)建事務(wù) advisor 并創(chuàng)建目標對象代理類
目標方法執(zhí)行時將會被 TransactionInterceptor 攔截
TransactionInterceptor 會委派 TransactionManager 執(zhí)行事務(wù)的創(chuàng)建,事務(wù)提交,事務(wù)回滾的動作
TransactionManager 會根據(jù)當前方法配置的事務(wù)傳播性及當前上下文是否存在事務(wù)來判斷是否新建事務(wù)
TransactionManager 當新建事務(wù)時會將事務(wù)綁定到當前上下文,以保證目標方法執(zhí)行時獲取的事務(wù)為同一實例
TransactionManager 執(zhí)行事務(wù)掛起時會將當前事務(wù)與當前上下文解除綁定關(guān)系
TransactionManager 執(zhí)行事務(wù)恢復(fù)時會將已掛起的事務(wù)重新與當前上下文綁定
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/73618.html
摘要:接下來繼續(xù)介紹三種架構(gòu)模式,分別是查詢分離模式微服務(wù)模式多級緩存模式。分布式應(yīng)用程序可以基于實現(xiàn)諸如數(shù)據(jù)發(fā)布訂閱負載均衡命名服務(wù)分布式協(xié)調(diào)通知集群管理選舉分布式鎖和分布式隊列等功能。 SpringCloud 分布式配置 SpringCloud 分布式配置 史上最簡單的 SpringCloud 教程 | 第九篇: 服務(wù)鏈路追蹤 (Spring Cloud Sleuth) 史上最簡單的 S...
摘要:它就是史上最簡單的教程第三篇服務(wù)消費者后端掘金上一篇文章,講述了通過去消費服務(wù),這篇文章主要講述通過去消費服務(wù)。概覽和架構(gòu)設(shè)計掘金技術(shù)征文后端掘金是基于的一整套實現(xiàn)微服務(wù)的框架。 Spring Boot 配置文件 – 在坑中實踐 - 后端 - 掘金作者:泥瓦匠鏈接:Spring Boot 配置文件 – 在坑中實踐版權(quán)歸作者所有,轉(zhuǎn)載請注明出處本文提綱一、自動配置二、自定義屬性三、ran...
摘要:是通過判斷當前是否匹配,只有匹配的才會創(chuàng)建代理。實現(xiàn)分析類結(jié)構(gòu)從上圖類結(jié)構(gòu),我們知道其實現(xiàn)與類似都是通過實現(xiàn)接口在完成實例化后進行自動代理處理。 概述 在上一篇 重拾-Spring AOP 中我們會發(fā)現(xiàn) Spring AOP 是通過類 ProxyFactoryBean 創(chuàng)建代理對象,其有個缺陷就是只能代理一個目標對象 bean, 當代理目標類過多時,配置文件臃腫不方便管理維護,因此 S...
摘要:探究系統(tǒng)登錄驗證碼的實現(xiàn)后端掘金驗證碼生成類手把手教程后端博客系統(tǒng)第一章掘金轉(zhuǎn)眼間時間就從月份到現(xiàn)在的十一月份了。提供了與標準不同的工作方式我的后端書架后端掘金我的后端書架月前本書架主要針對后端開發(fā)與架構(gòu)。 Spring Boot干貨系列總綱 | 掘金技術(shù)征文 - 掘金原本地址:Spring Boot干貨系列總綱博客地址:http://tengj.top/ 前言 博主16年認識Spin...
摘要:框架具有輕便,開源的優(yōu)點,所以本譯見構(gòu)建用戶管理微服務(wù)五使用令牌和來實現(xiàn)身份驗證往期譯見系列文章在賬號分享中持續(xù)連載,敬請查看在往期譯見系列的文章中,我們已經(jīng)建立了業(yè)務(wù)邏輯數(shù)據(jù)訪問層和前端控制器但是忽略了對身份進行驗證。 重拾后端之Spring Boot(四):使用JWT和Spring Security保護REST API 重拾后端之Spring Boot(一):REST API的搭建...
閱讀 2612·2021-11-15 11:38
閱讀 2626·2021-11-04 16:13
閱讀 18062·2021-09-22 15:07
閱讀 1025·2019-08-30 15:55
閱讀 3270·2019-08-30 14:15
閱讀 1672·2019-08-29 13:59
閱讀 3226·2019-08-28 18:28
閱讀 1582·2019-08-23 18:29