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