javascript
Spring TX源码分析
一、先思考一下
- 什么是事務(wù)?
事務(wù)是一系列數(shù)據(jù)庫(kù)操作的集合,在一個(gè)事務(wù)里,所有有關(guān)的數(shù)據(jù)庫(kù)操作一起提交或一起回滾 - 事務(wù)用在什么地方?
如果多個(gè)數(shù)據(jù)庫(kù)操作需要一起生效或一起失效,那么這些操作需要放在一個(gè)事務(wù)里面 - 事務(wù)如何創(chuàng)建?
用戶創(chuàng)建了針對(duì)數(shù)據(jù)庫(kù)操作的連接(java.sql.Connection)之后,就可以針對(duì)Connection進(jìn)行事務(wù)的操作,事務(wù)依賴于連接 - 事務(wù)的基本操作?
1. 開(kāi)啟事務(wù):Connection.setAutoCommit(false);關(guān)閉自動(dòng)提交則就開(kāi)啟了事務(wù)
2. 提交事務(wù):Connection.commit();
3. 回滾事務(wù):Connection.rollback();
關(guān)于事務(wù)的各種概念:
更詳細(xì)的參考
看源碼之前,如果有看AOP源碼的經(jīng)歷,會(huì)很有幫助,因?yàn)閟pring的事務(wù)就是基于AOP的
二、開(kāi)啟事務(wù)
我們?cè)赽eans.xml中開(kāi)啟事務(wù)的應(yīng)用,需要添加
<tx:annotation-driven transaction-manager="transactionManager"/>- tx:annotation-driven/注解的分析
但凡這種注解,都有對(duì)應(yīng)的解析器,跟AOP功能的源碼一樣,解析器都實(shí)現(xiàn)了NamespaceHandlerSupport類,我們來(lái)獲取下NamespaceHandlerSupport的實(shí)現(xiàn)類都有哪些
看名字就是TxNamespaceHandler類,我們來(lái)看下這個(gè)類有哪些內(nèi)容
- TxNamespaceHandler
Spring會(huì)默認(rèn)調(diào)用其init()方法,annotation-driven對(duì)應(yīng)的是AnnotationDrivenBeanDefinitionParser解析器,我們來(lái)看下這個(gè)解析器的作用
- AnnotationDrivenBeanDefinitionParser的作用分析
- AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
總結(jié):通過(guò)以上的分析可知,tx:annotation-driven/的主要功能就是將以下四個(gè)類注冊(cè)到Spring容器中
- AnnotationTransactionAttributeSource
- TransactionInterceptor(主要的攔截功能都在這里實(shí)現(xiàn))
- BeanFactoryTransactionAttributeSourceAdvisor(創(chuàng)建bean的代理類的時(shí)候該Advisor會(huì)被用上)
- InfrastructureAdvisorAutoProxyCreator:用于創(chuàng)建事務(wù)代理
- InfrastructureAdvisorAutoProxyCreator功能分析
其實(shí)現(xiàn)了BeanPostProcessor接口,則Spring在創(chuàng)建bean的時(shí)候,會(huì)默認(rèn)調(diào)用InfrastructureAdvisorAutoProxyCreator的postProcessAfterInitialization()方法,然后就是wrapIfNecessary,跟AOP的流程完全一致
可以看到,注冊(cè)了AutoProxyRegistrar(是一個(gè)BeanPostProcessor)和ProxyTransactionManagementConfiguration(用于解析事務(wù)屬性)
- ProxyTransactionManagementConfiguration
- 該類是一個(gè)配置Bean
- 目的:創(chuàng)建事務(wù)的切面,跟AOP的區(qū)別就在這里,AOP的切面是我們自己定義的,而事務(wù)的切面是Spring給我們生成的
- AutoProxyRegistrar
可以看到也注冊(cè)了一個(gè)InfrastructureAdvisorAutoProxyCreator
跟xml的方式殊途同歸:也實(shí)現(xiàn)了BeanPostProcessor接口,則Spring在創(chuàng)建bean的時(shí)候,會(huì)默認(rèn)調(diào)用InfrastructureAdvisorAutoProxyCreator的postProcessAfterInitialization()方法,然后就是wrapIfNecessary,跟AOP的流程完全一致
那么后置處理bean有了,切面也有了,就可以創(chuàng)建事務(wù)代理了
三、執(zhí)行事務(wù)
跟AOP一樣,以JdkDynamicProxy一樣,調(diào)用代理,觸發(fā)ReflectiveMethodInvocation的執(zhí)行方法
retVal = invocation.proceed();進(jìn)而執(zhí)行到
代碼跟到這里,跟AOP又有一個(gè)不同的點(diǎn),就是這個(gè)invoke的實(shí)現(xiàn)是TransactionInterceptor類,該類專門處理事務(wù)切面的執(zhí)行
//TransactionInterceptorpublic Object invoke(MethodInvocation invocation) throws Throwable {//計(jì)算出目標(biāo)類:可能是 {@code null}。 TransactionAttributeSource 應(yīng)該傳遞目標(biāo)類以及方法,該方法可能來(lái)自接口。Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);//適配TransactionAspectSupport的invokeWithinTransaction...return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);}進(jìn)而進(jìn)入invokeWithinTransaction
@Nullable//TransactionAspectSupportprotected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {// If the transaction attribute is null, the method is non-transactional.// 如果事務(wù)屬性為空,則該方法是非事務(wù)性的。TransactionAttributeSource tas = getTransactionAttributeSource();//拿到事務(wù)5個(gè)屬性的值,@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT )final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);//獲取事務(wù)管理器,由我們通過(guò)配置指定。事務(wù)管理的底層一定會(huì)與數(shù)據(jù)庫(kù)有關(guān),所以會(huì)注入數(shù)據(jù)源等屬性final PlatformTransactionManager tm = determineTransactionManager(txAttr);//切入點(diǎn),也就是需要控制事務(wù)的目的方法(update...)final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// Standard transaction demarcation with getTransaction and commit/rollback calls.//使用 getTransaction 和 commitRollback 調(diào)用進(jìn)行標(biāo)準(zhǔn)事務(wù)劃分,這一句是最難理解的//ifNecessary?其實(shí)就是根據(jù)事務(wù)屬性決定是否開(kāi)啟事務(wù)TransactionInfo txInfo = createTransactionIfNecessary(tm, 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.// 源方法運(yùn)行retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// target invocation exception//出現(xiàn)異常時(shí)則回滾事務(wù),注意:如果是Exception不會(huì)回滾,只有RunTimeException或者Error來(lái)及其子類才會(huì)回滾completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {//重置 TransactionInfo ThreadLocal。 在所有情況下都調(diào)用它:異常或正常返回cleanupTransactionInfo(txInfo);}//提交事務(wù)//TransactionManager調(diào)用Connection.commit()commitTransactionAfterReturning(txInfo);return retVal;}進(jìn)入createTransactionIfNecessary
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {// If no name specified, apply method identification as transaction name.// 如果未指定名稱,則應(yīng)用方法標(biāo)識(shí)作為事務(wù)名稱if (txAttr != null && txAttr.getName() == null) {txAttr = new DelegatingTransactionAttribute(txAttr) {@Overridepublic String getName() {return joinpointIdentification;}};}//記錄當(dāng)前事務(wù)的狀態(tài),是否新事務(wù),是否只讀事務(wù),是否開(kāi)啟同步?TransactionStatus status = null;if (txAttr != null) {if (tm != null) {//獲取事務(wù)的狀態(tài)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);}當(dāng)TransactionAttribute為null,則創(chuàng)建一個(gè)TransactionInfo
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, String joinpointIdentification,@Nullable TransactionStatus status) {/** 根據(jù)給定的事務(wù)屬性、事務(wù)管理器、方法連接點(diǎn)描述字符串(全限定方法名)信息創(chuàng)建一個(gè)事務(wù)信息對(duì)象TransactionInfo*/TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);// 如果事務(wù)屬性不為nullif (txAttr != null) {// We need a transaction for this method...if (logger.isTraceEnabled()) {logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");}//那么設(shè)置事務(wù)狀態(tài),這里就表示為當(dāng)前方法創(chuàng)建了一個(gè)事務(wù)txInfo.newTransactionStatus(status);}// 如果事務(wù)屬性為null,那么表示當(dāng)前方法必須要?jiǎng)?chuàng)建事務(wù)else {if (logger.isTraceEnabled()) {logger.trace("No need to create transaction for [" + joinpointIdentification +"]: This method is not transactional.");}}//始終將最新的TransactionInfo綁定到當(dāng)前線程,即使我們沒(méi)有在此處創(chuàng)建新的事務(wù)也是如此。//也就是將當(dāng)前線程的最新事務(wù)棧設(shè)置為當(dāng)前對(duì)象存入transactionInfoHolder中//這保證即使此方面未創(chuàng)建任何事務(wù),也將正確管理TransactionInfo堆棧。txInfo.bindToThread();return txInfo; }- 這里要先介紹一下TransactionInfo,這是SpringTX的核心,TransactionInfo是TransactionAspectSupport的內(nèi)部類,用來(lái)保存線程的執(zhí)行方法時(shí)的事務(wù)信息。內(nèi)部保存了事務(wù)管理器transactionManager、事務(wù)屬性transactionAttribute、全路徑方法名joinpointIdentification。還保存了當(dāng)前方法的事務(wù)transactionStatus,以及前一個(gè)方法的事務(wù)信息對(duì)象oldTransactionInfo。
如果事務(wù)屬性不為空,那么走getTransaction獲取/開(kāi)啟事務(wù)分支。在createTransactionIfNecessary方法中,如果存在事務(wù)屬性TransactionAttribute,并且存在事務(wù)管理器PlatformTransactionManager,那么將調(diào)用事務(wù)管理器的getTransaction方法根據(jù)為當(dāng)前方法配置的事務(wù)定義的屬性嘗試獲取事務(wù),將返回一個(gè)TransactionStatus對(duì)象。該方法是事務(wù)管理的核心方法,其骨干實(shí)現(xiàn)位于抽象實(shí)現(xiàn)類AbstractPlatformTransactionManager中,該方法根據(jù)配置的各種事務(wù)傳播行為做出不同的處理,方法執(zhí)行完畢將可能開(kāi)啟了新事物,也可能沒(méi)有開(kāi)啟,甚至拋出異常。AbstractPlatformTransactionManager的設(shè)計(jì)基于模版方法模式,他提供了處理流程,并且提供了一系列的do……模版方法供子類來(lái)實(shí)現(xiàn)自己的邏輯。
getTransaction方法的大概邏輯為:
-
- 如果條件都滿足,那么表示此前已經(jīng)開(kāi)啟過(guò)了事務(wù),即存在外層事務(wù),隨后調(diào)用handleExistingTransaction方法統(tǒng)一處理這種情況,比如加入當(dāng)前事務(wù)、新建事務(wù)、拋出異常等等邏輯。
-
- 校驗(yàn)為當(dāng)前事務(wù)方法設(shè)置的事務(wù)超時(shí)時(shí)間,如果小于默認(rèn)超時(shí)時(shí)間(-1),將會(huì)拋出異常。
-
- 如果為當(dāng)前事務(wù)方法設(shè)置的傳播行為PROPAGATION_MANDATORY,該傳播行為的含義是:如果當(dāng)前存在事務(wù),則當(dāng)前方法加入到該事務(wù)中去,如果當(dāng)前不存在事務(wù),則當(dāng)前方法直接拋出異常。這里由于不存在外層事務(wù),那么這里就直接拋出異常:“No existing transaction found for transaction marked with propagation ‘mandatory’”。
-
- 如果為當(dāng)前事務(wù)方法設(shè)置的傳播行為是PROPAGATION_REQUIRED或者PROPAGATION_REQUIRES_NEW或者PROPAGATION_NESTED,這些傳播行為的含義的共同點(diǎn)之一就是:如果當(dāng)前不存在事務(wù),就創(chuàng)建一個(gè)新事務(wù)運(yùn)行。這里由于不存在外層事務(wù),那么這里就直接創(chuàng)建一個(gè)新事物。
-
-
- 首先調(diào)用suspend方法首先掛起事務(wù)同步,然后再委派給doSuspend模板方法掛起事務(wù),將返回被掛起的資源,用于后續(xù)恢復(fù),如果沒(méi)有事務(wù)同步也沒(méi)有事務(wù),那么將返回null。這里由于沒(méi)有已存在的事務(wù),那么參數(shù)傳遞null,一般也會(huì)將返回null。
-
-
-
- 調(diào)用startTransaction方法,內(nèi)部依次調(diào)用newTransactionStatus方法創(chuàng)建TransactionStatus、doBegin方法真正的開(kāi)啟新的事務(wù)、prepareSynchronization方法準(zhǔn)備事務(wù)同步,最終返回TransactionStatus,該對(duì)象包含了創(chuàng)建的內(nèi)部事務(wù)對(duì)象,以及其他事務(wù)信息。
-
-
- 調(diào)用prepareTransactionStatus方法返回一個(gè)TransactionStatus,和上面的startTransaction方法相比,其內(nèi)部會(huì)調(diào)用newTransactionStatus和prepareSynchronization,但不會(huì)調(diào)用doBegin方法,因此不會(huì)真正的開(kāi)啟事物。這里它的newTransaction參數(shù)為false,suspendedResources參數(shù)為null。
- doGetTransaction獲取事務(wù)連接
返回當(dāng)前已存在的事務(wù)對(duì)象,返回的對(duì)象應(yīng)包含有關(guān)任何現(xiàn)有事務(wù)的信息,即,在事務(wù)管理器上的當(dāng)前getTransaction方法調(diào)用之前已經(jīng)啟動(dòng)的事務(wù)。因此,doGetTransaction的實(shí)現(xiàn)通常是將查找現(xiàn)有事務(wù)并將相應(yīng)的狀態(tài)存儲(chǔ)在返回的事務(wù)對(duì)象中。返回的對(duì)象通常特定于具體的事務(wù)管理器子類自己實(shí)現(xiàn)。一般都使用DataSourceTransactionManager這個(gè)事務(wù)管理器,它的doGetTransaction方法邏輯如下:
- 這里也要介紹一個(gè)類TransactionSynchronizationManager事務(wù)同步管理器
這個(gè)類比較特別,雖然它的名字帶有Transaction以及Manager,但卻不是TransactionManager體系,它沒(méi)有任何繼承樹(shù),因此它可以看作一個(gè)很特別的工具類。該類被稱為事務(wù)同步管理器。主要用于管理每一個(gè)線程當(dāng)前所使用的數(shù)據(jù)庫(kù)事務(wù)連接資源和事務(wù)同步器(TransactionSynchronization)。
??一個(gè)線程在當(dāng)前只能激活一個(gè)連接資源,因此如果需要綁定新的連接資源,那么需要將此前綁定的資源刪除(或者說(shuō)保存起來(lái),等新資源使用完畢之后再恢復(fù)此前的連接資源)。另外還能支持事務(wù)同步列表,事務(wù)同步必須由事務(wù)管理器通過(guò)initSynchronization()和clearSynchronization()來(lái)進(jìn)行激活和停用,使用isSynchronizationActive()檢測(cè)當(dāng)前是否具有事務(wù)同步。
??TransactionSynchronizationManager內(nèi)部是通過(guò)很多ThreadLocal(線程本地變量)類型的屬性來(lái)實(shí)現(xiàn)為當(dāng)前線程維護(hù)自己的資源的功能的,實(shí)現(xiàn)資源隔離。
部分屬性:
public abstract class TransactionSynchronizationManager {private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);// 事務(wù)資源// 一個(gè)ThreadLocal屬性,用于存放線程當(dāng)前使用的數(shù)據(jù)庫(kù)資源// value是一個(gè)Map<Object, Object>,key為某個(gè)數(shù)據(jù)源DataSource ,value實(shí)際上就是連接ConnectionHolderprivate static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");/*** 事務(wù)同步* <p>* 一個(gè)ThreadLocal屬性,用于存放線程當(dāng)前激活的事務(wù)同步器TransactionSynchronization* 每個(gè)線程都可以開(kāi)啟多個(gè)事物同步,用于在處理事務(wù)的各個(gè)階段進(jìn)行自定義擴(kuò)展或者回調(diào)* <p>* TransactionSynchronization的同步回調(diào)功能類似于此前學(xué)習(xí)的@TransactionalEventListener*/private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =new NamedThreadLocal<>("Transaction synchronizations");//一個(gè)ThreadLocal屬性,用于存放線程當(dāng)前的事務(wù)的名稱private static final ThreadLocal<String> currentTransactionName =new NamedThreadLocal<>("Current transaction name");//一個(gè)ThreadLocal屬性,用于存放線程當(dāng)前的事務(wù)的只讀狀態(tài)private static final ThreadLocal<Boolean> currentTransactionReadOnly =new NamedThreadLocal<>("Current transaction read-only status");//一個(gè)ThreadLocal屬性,用于存放線程當(dāng)前的當(dāng)前事務(wù)的隔離級(jí)別private static final ThreadLocal<Integer> currentTransactionIsolationLevel =new NamedThreadLocal<>("Current transaction isolation level");//一個(gè)ThreadLocal屬性,用于存放線程當(dāng)前是否開(kāi)啟了事務(wù)private static final ThreadLocal<Boolean> actualTransactionActive =new NamedThreadLocal<>("Actual transaction active");- obtainDataSource獲取數(shù)據(jù)源
獲取當(dāng)前DataSourceTransactionManager實(shí)際使用的數(shù)據(jù)源,就是我們配置給事務(wù)管理器的DataSource
/*** @return 數(shù)據(jù)源(絕不為null)*/ protected DataSource obtainDataSource() {DataSource dataSource = getDataSource();Assert.state(dataSource != null, "No DataSource set");return dataSource; } /*** 就是我們配置的數(shù)據(jù)源*/ @Nullable private DataSource dataSource; /*** 返回此事務(wù)管理器內(nèi)部的JDBC DataSource。*/ @Nullable public DataSource getDataSource() {return this.dataSource; }- getResource獲取已存在的連接
TransactionSynchronizationManager的方法,該方法檢索給定key綁定到當(dāng)前線程的資源,實(shí)際上就是嘗試從resources屬性中獲綁定當(dāng)前線程的從給定數(shù)據(jù)源獲已取到的連接資源ConnectionHolder。如果此前沒(méi)有獲取過(guò)此數(shù)據(jù)源的連接,那么將會(huì)得到一個(gè)null值,即如果是第一次進(jìn)入事務(wù)方法,那么將返回null。這里resources屬性的value是一個(gè)Map<Object, Object>,這說(shuō)明一個(gè)線程可以從不同的數(shù)據(jù)源中獲取資源,但是對(duì)于同一個(gè)數(shù)據(jù)源,只能保存一個(gè)的數(shù)據(jù)源。
public static Object getResource(Object key) {//如有必要,解開(kāi)給定的資源句柄,否則按原樣返回給定的句柄,常用于從各種代理對(duì)象中獲取原始對(duì)象Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);//真正獲取當(dāng)前綁定的資源Object value = doGetResource(actualKey);if (value != null && logger.isTraceEnabled()) {logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +Thread.currentThread().getName() + "]");}return value;}- isExistingTransaction是否已存在事務(wù)
檢查是否已經(jīng)開(kāi)啟過(guò)事務(wù),默認(rèn)返回false,被具體的事務(wù)管理器子類重寫。
DataSourceTransactionManager的邏輯很簡(jiǎn)單,判斷通過(guò)doGetTransaction方法獲取的DataSourceTransactionObject內(nèi)部的數(shù)據(jù)庫(kù)連接connectionHolder屬性是否不為null,并且是否已經(jīng)開(kāi)啟了事務(wù)。我們說(shuō)過(guò)如果當(dāng)前線程是第一次進(jìn)來(lái),那么connectionHolder就是null。
- suspend掛起事務(wù)
掛起給定的事務(wù)。首先掛起當(dāng)前線程的事務(wù)同步回調(diào),然后再委派給doSuspend模板方法由子類來(lái)實(shí)現(xiàn)掛起當(dāng)前事務(wù),并且還會(huì)清空TransactionSynchronizationManager中保存的當(dāng)前線程的事務(wù)信息。最終將會(huì)返回會(huì)被掛起的資源只有者SuspendedResourcesHolder。如果沒(méi)有事務(wù)同步也沒(méi)有事務(wù),那么將會(huì)返回null。當(dāng)前線程第一次進(jìn)入事務(wù)方法時(shí),默認(rèn)將會(huì)返回null。
//AbstractPlatformTransactionManagerprotected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {//如果當(dāng)前線程的事務(wù)同步處于活動(dòng)狀態(tài),即存在綁定的TransactionSynchronization,則返回true。//如果是第一次因?yàn)檫M(jìn)來(lái),那么自然為falseif (TransactionSynchronizationManager.isSynchronizationActive()) {//掛起當(dāng)前線程的所有事務(wù)同步回調(diào),這類似于@TransactionalEventListener,并返回"被掛起"的回調(diào)List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();try {Object suspendedResources = null;if (transaction != null) {//掛起事務(wù),由具體的子類實(shí)現(xiàn)suspendedResources = doSuspend(transaction);}//獲取當(dāng)前事務(wù)的信息,并且清空各個(gè)ThreadLocal緩存中的當(dāng)前線程的當(dāng)前事務(wù)信息(恢復(fù)為默認(rèn)值)//獲取并清空(設(shè)置為null)事物名稱String name = TransactionSynchronizationManager.getCurrentTransactionName();TransactionSynchronizationManager.setCurrentTransactionName(null);//獲取并清空(設(shè)置為false)事物只讀狀態(tài)boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);//獲取并清空(設(shè)置為null)事物隔離級(jí)別Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);//獲取并清空(設(shè)置為false)事物是否激活boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();TransactionSynchronizationManager.setActualTransactionActive(false);//將獲取的當(dāng)前事物的信息存入一個(gè)SuspendedResourcesHolder對(duì)象中返回return new SuspendedResourcesHolder(suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);}catch (RuntimeException | Error ex) {// doSuspend failed - original transaction is still active...doResumeSynchronization(suspendedSynchronizations);throw ex;}}//如果沒(méi)有事務(wù)同步但是開(kāi)啟了事務(wù),那么掛起事務(wù)else if (transaction != null) {// Transaction active but no synchronization active.//掛起事務(wù),由具體的子類實(shí)現(xiàn)Object suspendedResources = doSuspend(transaction);//將掛起的資源存入一個(gè)SuspendedResourcesHolder對(duì)象中返回return new SuspendedResourcesHolder(suspendedResources);}else {// Neither transaction nor synchronization active.//事務(wù)或者事務(wù)同步均未激活,返回null,什么也不干return null;}}- isSynchronizationActive是否激活事務(wù)同步
?
TransactionSynchronizationManager的方法,用來(lái)判斷線程在當(dāng)前是否已激活事務(wù)同步TransactionSynchronization。實(shí)際上就是synchronizations屬性中是否有綁定到當(dāng)前線程的Set集合,如果有(不為null),那就說(shuō)明存在事務(wù)同步。
- doSuspendSynchronization掛起事務(wù)同步
??
??該方法掛起當(dāng)前線程在TransactionSynchronizationManager的synchronize并且將屬性中為當(dāng)前線程保持的事務(wù)同步列表引用移除,最后返回被掛起的事務(wù)同步列表!
- doSuspend掛起事務(wù)
其核心就是doSuspend方法,該方法默認(rèn)拋出異常,由子類自己實(shí)現(xiàn)!DataSourceTransactionManager重寫的方法很簡(jiǎn)單,就是將DataSourceTransactionObject中的connectionHolder設(shè)置為null,并且將給定數(shù)據(jù)源綁定到當(dāng)前線程的連接資源從TransactionSynchronizationManager的resources屬性中移除并返回,這就是DataSourceTransactionManager被掛起的連接資源,就是此前獲取的連接。從這里能夠看出,所謂的“掛起”,就是將當(dāng)前的連接從綁定的線程本地變量中移除!
/*** DataSourceTransactionManager的方法* <p>* 掛起當(dāng)前事務(wù),返回當(dāng)前的連接資源** @param transaction 掛起事務(wù)* @return 被掛起的資源*/ @Override protected Object doSuspend(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;//將當(dāng)前的事務(wù)對(duì)象的connectionHolder設(shè)置為nulltxObject.setConnectionHolder(null);//將當(dāng)前線程的綁定的當(dāng)前數(shù)據(jù)源對(duì)應(yīng)的連接同樣移除,并且返回被移除的連接資源return TransactionSynchronizationManager.unbindResource(obtainDataSource()); }/*** TransactionSynchronizationManager的方法* <p>* 移除當(dāng)前線程中給定key綁定的資源的值** @param key 就是當(dāng)前數(shù)據(jù)源* @return 被移除的資源,就是連接*/ public static Object unbindResource(Object key) throws IllegalStateException {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);//真正的移除指定的key對(duì)應(yīng)的連接Object value = doUnbindResource(actualKey);if (value == null) {throw new IllegalStateException("No value for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");}return value; }/*** 事務(wù)資源*/ private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");/*** TransactionSynchronizationManager的方法* <p>* 移除當(dāng)前線程中給定key綁定的資源的值。** @param actualKey 就是當(dāng)前數(shù)據(jù)源* @return 被移除的資源,就是連接*/ @Nullable private static Object doUnbindResource(Object actualKey) {//獲取和當(dāng)前線程綁定的數(shù)據(jù)庫(kù)資源mapMap<Object, Object> map = resources.get();if (map == null) {return null;}//從map中移除從當(dāng)前數(shù)據(jù)源對(duì)應(yīng)的連接緩存Object value = map.remove(actualKey);//如果map為空,則刪除整個(gè)ThreadLocal。if (map.isEmpty()) {resources.remove();}// Transparently suppress a ResourceHolder that was marked as void...if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {value = null;}if (value != null && logger.isTraceEnabled()) {logger.trace("Removed value [" + value + "] for key [" + actualKey + "] from thread [" +Thread.currentThread().getName() + "]");}//返回被移除的資源return value; }- newTransactionStatus開(kāi)啟新事物
新建一個(gè)DefaultTransactionStatus實(shí)現(xiàn)并返回,內(nèi)部持有我們?yōu)楫?dāng)前方法配置的事務(wù)屬性或者默認(rèn)屬性,以及保存著此前掛起的其他資源。新建一個(gè)DefaultTransactionStatus實(shí)現(xiàn)并返回,內(nèi)部持有我們?yōu)楫?dāng)前方法配置的事務(wù)屬性或者默認(rèn)屬性,以及保存著此前掛起的其他資源。
/*** AbstractPlatformTransactionManager的方法* <p>* 為給定參數(shù)新創(chuàng)建一個(gè)TransactionStatus實(shí)例,實(shí)際類型為DefaultTransactionStatus** @param definition 為當(dāng)前方法配置的事務(wù)定義* @param transaction 獲取的事務(wù)對(duì)象* @param newTransaction 是否是新事物* @param newSynchronization 是否開(kāi)啟事務(wù)同步* @param debug 是否支持debug級(jí)別的日志* @param suspendedResources 被掛起的資源,比如此前的事務(wù)同步* @return DefaultTransactionStatus對(duì)象*/ protected DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {//如果newSynchronization為true并且當(dāng)前線程沒(méi)有綁定的事務(wù)同步,那么確定開(kāi)啟新事物同步//由于此前調(diào)用了suspend方法清理了此前的事務(wù)同步,因此一般都是需要開(kāi)啟新事務(wù)同步,即為trueboolean actualNewSynchronization = newSynchronization &&!TransactionSynchronizationManager.isSynchronizationActive();//返回一個(gè)新建的DefaultTransactionStatus對(duì)象,該對(duì)象被用來(lái)表示新開(kāi)啟的事務(wù),是TransactionStatus的默認(rèn)實(shí)現(xiàn)//內(nèi)部包括了各種新開(kāi)啟的事務(wù)狀態(tài),當(dāng)然包括此前掛起的事務(wù)的資源return new DefaultTransactionStatus(transaction, newTransaction, actualNewSynchronization,definition.isReadOnly(), debug, suspendedResources); } /*DefaultTransactionStatus的屬性*/ /*** 從事務(wù)管理器獲取的內(nèi)部事務(wù)* 對(duì)于DataSourceTransactionManager來(lái)說(shuō)就是DataSourceTransactionObject*/ @Nullable private final Object transaction; /*** 是否是新事物*/ private final boolean newTransaction; /*** 是否開(kāi)啟新事務(wù)同步*/ private final boolean newSynchronization; /*** 是否開(kāi)啟新事務(wù)同步*/ private final boolean readOnly; /*** 是否支持debug日志級(jí)別*/ private final boolean debug; /*** 此前被掛起的事務(wù)資源*/ @Nullable private final Object suspendedResources; public DefaultTransactionStatus(@Nullable Object transaction, boolean newTransaction, boolean newSynchronization,boolean readOnly, boolean debug, @Nullable Object suspendedResources) {this.transaction = transaction;this.newTransaction = newTransaction;this.newSynchronization = newSynchronization;this.readOnly = readOnly;this.debug = debug;this.suspendedResources = suspendedResources; }- doBegin真正開(kāi)啟事務(wù)
該方法是核心方法,當(dāng)事務(wù)管理器決定實(shí)際開(kāi)始新事務(wù)時(shí),將調(diào)用此方法。此時(shí)之前可能沒(méi)有任何事務(wù),或者先前的事務(wù)已被暫停。根據(jù)給定的事務(wù)定義TransactionDefinition,以及此前通過(guò)doGetTransaction方法返回的事務(wù)對(duì)象(也就是DataSourceTransactionObject),使用給定的語(yǔ)義開(kāi)始一個(gè)新事務(wù)。不必關(guān)心應(yīng)用傳播行為,因?yàn)槌橄笫聞?wù)管理器已經(jīng)處理了該行為。
??
大概步驟為:
- prepareConnectionForTransaction準(zhǔn)備事務(wù)連接
使用給定的事務(wù)語(yǔ)義準(zhǔn)備給定的Connection,就是將我們?cè)O(shè)置的隔離級(jí)別isolationLevel,只讀標(biāo)志readOnly屬性賦給當(dāng)前事務(wù)連接。如果我們配置的隔離級(jí)別屬性是ISOLATION_DEFAULT,即采用默認(rèn)隔離級(jí)別,或者不是默認(rèn)的隔離級(jí)別但是與連接的隔離級(jí)別一致,那么將返回null,否則將設(shè)置連接的隔離級(jí)別為指定的級(jí)別,并且返回從連接中獲取的隔離級(jí)別(如果有)。
/*** DataSourceUtils的方法* <p>* 使用給定的事務(wù)語(yǔ)義準(zhǔn)備給定的Connection。** @param con 需要準(zhǔn)備的連接* @param definition 適用的事務(wù)定義* @return 連接先前的隔離級(jí)別,可能為null*/ @Nullable public static Integer prepareConnectionForTransaction(Connection con, @Nullable TransactionDefinition definition)throws SQLException {Assert.notNull(con, "No Connection specified");boolean debugEnabled = logger.isDebugEnabled();// 設(shè)置只讀標(biāo)志。if (definition != null && definition.isReadOnly()) {try {if (debugEnabled) {logger.debug("Setting JDBC Connection [" + con + "] read-only");}//設(shè)置連接只讀屬性con.setReadOnly(true);} catch (SQLException | RuntimeException ex) {Throwable exToCheck = ex;while (exToCheck != null) {if (exToCheck.getClass().getSimpleName().contains("Timeout")) {// Assume it's a connection timeout that would otherwise get lost: e.g. from JDBC 4.0throw ex;}exToCheck = exToCheck.getCause();}// "read-only not supported" SQLException -> ignore, it's just a hint anywaylogger.debug("Could not set JDBC Connection read-only", ex);}}// 應(yīng)用特定的隔離級(jí)別(如果有)。Integer previousIsolationLevel = null;//如果存在隔離級(jí)別并且不等于默認(rèn)配置,即不等于ISOLATION_DEFAULT(該級(jí)別的意思是使用數(shù)據(jù)庫(kù)的默認(rèn)級(jí)別)if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {if (debugEnabled) {logger.debug("Changing isolation level of JDBC Connection [" + con + "] to " +definition.getIsolationLevel());}//獲取當(dāng)前連接的隔離級(jí)別int currentIsolation = con.getTransactionIsolation();//如果手動(dòng)設(shè)置的隔離級(jí)別不等于連接的隔離級(jí)別if (currentIsolation != definition.getIsolationLevel()) {//記錄連接的隔離級(jí)別previousIsolationLevel = currentIsolation;//連接的隔離級(jí)別手動(dòng)設(shè)置為我們配置的隔離級(jí)別con.setTransactionIsolation(definition.getIsolationLevel());}}//返回此前的連接的隔離級(jí)別,可能為nullreturn previousIsolationLevel; }- prepareTransactionalConnection優(yōu)化只讀事務(wù)
??事務(wù)開(kāi)始后立即準(zhǔn)備事務(wù)連接,主要是對(duì)于只讀事務(wù)的優(yōu)化操作(需要手動(dòng)開(kāi)啟)。如果將事務(wù)管理器的"enforceReadOnly"標(biāo)志設(shè)置為true(默認(rèn)為false),并且事務(wù)定義指示只讀事務(wù),則默認(rèn)實(shí)現(xiàn)將執(zhí)行"SET TRANSACTION READ ONLY"這一個(gè)sql語(yǔ)句。
??"SET TRANSACTION READ ONLY"這個(gè)sql的意思就是告訴數(shù)據(jù)庫(kù),此事務(wù)中的后續(xù)sql語(yǔ)句將只有查詢操作,不能進(jìn)行DML操作。在"SET TRANSACTION READ ONLY"之后的查詢語(yǔ)句將不會(huì)查詢到該事物期間提交的內(nèi)容,只能查詢到事務(wù)開(kāi)始之前提交的內(nèi)容,相當(dāng)于查詢一個(gè)快照。進(jìn)行只讀事務(wù)設(shè)置之后,將有效減輕數(shù)據(jù)庫(kù)壓力。對(duì)于同一個(gè)表進(jìn)行更新操作時(shí),只讀事務(wù)不會(huì)被阻塞,可以正常的執(zhí)行查詢操作,在只讀事務(wù)操作期間也不會(huì)影響其他事務(wù)!
上面說(shuō)了這么多好處,很遺憾的是DataSourceTransactionManager的enforceReadOnly屬性默認(rèn)為false,并且大部分開(kāi)發(fā)者也不知道這個(gè)優(yōu)化,因此大多數(shù)情況下并不會(huì)執(zhí)行該sql語(yǔ)句,即不會(huì)進(jìn)行優(yōu)化。如果要開(kāi)啟,那么可以這么設(shè)置:
/*** 配置DataSourceTransactionManager* 用于管理某一個(gè)數(shù)據(jù)庫(kù)的事務(wù)*/ @Bean public DataSourceTransactionManager transactionManager() {DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(druidDataSource());//設(shè)置只讀事務(wù)優(yōu)化dataSourceTransactionManager.setEnforceReadOnly(true);//傳入一個(gè)數(shù)據(jù)源return dataSourceTransactionManager; }當(dāng)然如果你真的這么開(kāi)啟了,并且你通過(guò)@Transactional注解設(shè)置了某個(gè)方法的事務(wù)的readOnly屬性為true,那么確實(shí)會(huì)執(zhí)行該方法,但是你講將會(huì)收到一個(gè)異常:“Connection is read-only. Queries leading to data modification are not allowed”。原因是什么呢?很簡(jiǎn)單,在前面的prepareConnectionForTransaction方法中,連接被設(shè)置為只讀,然而在隨后的prepareTransactionalConnection方法中,執(zhí)行該sql語(yǔ)句的卻是executeUpdate方法,自然會(huì)拋出異常!所以說(shuō),這個(gè)優(yōu)化還不能隨便開(kāi)?;蛘哒f(shuō),是因?yàn)椴煌臄?shù)據(jù)庫(kù)對(duì)于Spring的readOnly屬性的支持是不一樣的,mysql支持Spring的readOnly參數(shù),即支持JDBC的con.setReadOnly(true),因此就沒(méi)必要再設(shè)置enforceReadOnly為true,而oracle則僅支持在Oracle server的設(shè)置而非JDBC驅(qū)動(dòng)的配置,因此不支持con.setReadOnly(true),所以實(shí)際上Spring的readOnly配置對(duì)于Oracle無(wú)效,所以O(shè)racle數(shù)據(jù)庫(kù)可以開(kāi)啟此優(yōu)化,mysql則不必要。
- determineTimeout確定超時(shí)時(shí)間
確定給定事務(wù)定義的實(shí)際超時(shí)時(shí)間。如果事務(wù)定義未指定非默認(rèn)值,則將使用默認(rèn)超時(shí)。
/*** AbstractPlatformTransactionManager的方法* <p>* 確定給定事務(wù)定義的實(shí)際超時(shí)時(shí)間。* 如果事務(wù)定義未指定非默認(rèn)值,則將使用默認(rèn)超時(shí)。** @param definition 事務(wù)定義* @return 實(shí)際使用的超時(shí)時(shí)間*/ protected int determineTimeout(TransactionDefinition definition) {//如果不是默認(rèn)超時(shí)時(shí)間,那么使用指定的事件if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {return definition.getTimeout();}//否則使用默認(rèn)超時(shí)return getDefaultTimeout(); }/*** AbstractPlatformTransactionManager的屬性* <p>* 默認(rèn)超時(shí)時(shí)間(秒),默認(rèn)值為-1,表示使用基礎(chǔ)事務(wù)系統(tǒng)的默認(rèn)超時(shí);*/ private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;public final int getDefaultTimeout() {return this.defaultTimeout; }- setTimeoutInSeconds設(shè)置超時(shí)deadline
這里實(shí)際上就是根據(jù)設(shè)置的值和當(dāng)前時(shí)間轉(zhuǎn)換為未來(lái)的毫秒值并創(chuàng)建新Date配置給deadline屬性,在其他數(shù)據(jù)庫(kù)操作框架具體操作時(shí)將會(huì)獲取并應(yīng)用該參數(shù)。比如mybatis,在執(zhí)行sql之前會(huì)獲取到超時(shí)時(shí)間,計(jì)算之后會(huì)通過(guò)Statement.setQueryTimeout方法來(lái)設(shè)置,也就是說(shuō)這個(gè)超時(shí)時(shí)間是執(zhí)行sql之前的代碼執(zhí)行時(shí)間+sql執(zhí)行時(shí)間,如果執(zhí)行時(shí)間超過(guò)了設(shè)置時(shí)間就會(huì)拋出異常,這個(gè)異常就會(huì)被spring事務(wù)切面捕獲到最終導(dǎo)致事務(wù)回滾,而如果在sql執(zhí)行完畢之后的方法處理時(shí)間超過(guò)了這個(gè)超時(shí)時(shí)間,那么是不會(huì)進(jìn)行回滾的,事務(wù)將會(huì)正常提交。
/*** ConnectionHolder的父類ResourceHolderSupport的方法* <p>* 設(shè)置此對(duì)象的超時(shí)(以秒為單位)。** @param seconds 到期前的秒數(shù)*/ public void setTimeoutInSeconds(int seconds) {setTimeoutInMillis(seconds * 1000L); }/*** ConnectionHolder的父類ResourceHolderSupport的屬性*/ @Nullable private Date deadline;/*** 設(shè)置此對(duì)象的超時(shí)(以毫秒為單位)。** @param millis 到期前的毫秒數(shù)*/ public void setTimeoutInMillis(long millis) {//根據(jù)當(dāng)前時(shí)間和超時(shí)時(shí)間計(jì)算出到期的Datethis.deadline = new Date(System.currentTimeMillis() + millis); }- bindResource綁定資源到resources
對(duì)于新獲取的連接資源會(huì)被綁定到TransactionSynchronizationManager的resources線程本地變量屬性中(resources我們?cè)诖饲熬鸵?jiàn)過(guò)了)。key就是當(dāng)前的屬性源DataSource,value就是ConnectionHolder。
/*** 事務(wù)資源*/ private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");/*** TransactionSynchronizationManager的方法* <p>* 將給定key的給定資源value綁定到當(dāng)前線程。* 對(duì)于DataSourceTransactionManager,key就是DataSource實(shí)例,value就是ConnectionHolder** @param key 將值綁定到的鍵(通常是資源工廠,比如dataSource)* @param value 要綁定的值(通常是活動(dòng)資源對(duì)象,比如數(shù)據(jù)庫(kù)連接)* @throws IllegalStateException 如果已經(jīng)有綁定到線程的值*/ public static void bindResource(Object key, Object value) throws IllegalStateException {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Assert.notNull(value, "Value must not be null");//獲取當(dāng)前線程的本地資源mapMap<Object, Object> map = resources.get();//如果找不到,則設(shè)置一個(gè)Mapif (map == null) {map = new HashMap<>();resources.set(map);}//將actualKey和value存入map中,返回舊的valueObject oldValue = map.put(actualKey, value);// Transparently suppress a ResourceHolder that was marked as void...if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {oldValue = null;}//如果已經(jīng)有綁定到線程的當(dāng)前key的值,則拋出異常if (oldValue != null) {throw new IllegalStateException("Already value [" + oldValue + "] for key [" +actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");}if (logger.isTraceEnabled()) {logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +Thread.currentThread().getName() + "]");} }- prepareSynchronization準(zhǔn)備事務(wù)同步
??
該方法通常用在doBegin開(kāi)啟新事物之后,用于準(zhǔn)備事務(wù)同步,就是將當(dāng)前事務(wù)的一系列屬性綁定到TransactionSynchronizationManager的對(duì)應(yīng)的線程本地變量中。
- prepareTransactionStatus準(zhǔn)備事務(wù)狀態(tài)
- prepareTransactionStatus方法和上面的startTransaction方法相比,其內(nèi)部會(huì)調(diào)用newTransactionStatus和prepareSynchronization,但不會(huì)調(diào)用doBegin方法,因此不會(huì)真正的開(kāi)啟事物。返回的TransactionStatus,其內(nèi)部保存了其他的資源,比如被掛起的事務(wù)信息。
- 如果是第一次進(jìn)入事務(wù)方法,即當(dāng)前方法是最外層事務(wù)方法,并且傳播行為是PROPAGATION_SUPPORTS或PROPAGATION_NEVER或PROPAGATION_NOT_SUPPORTED,那么會(huì)調(diào)用該方法,它的newTransaction參數(shù)為false,suspendedResources參數(shù)為null。即這些傳播行為都不會(huì)真正的開(kāi)啟數(shù)據(jù)庫(kù)級(jí)別的事務(wù)(不會(huì)獲取新的連接)。
- 如果是已存在外層事務(wù),即當(dāng)前方法是內(nèi)層事務(wù)方法,并且傳播行為是PROPAGATION_NOT_SUPPORTED或者PROPAGATION_NESTED或者PROPAGATION_SUPPORTS或者PROPAGATION_REQUIRED或者PROPAGATION_MANDATORY,那么也有可能調(diào)用這個(gè)方法,它的newTransaction參數(shù)為false,suspendedResources參數(shù)為被掛起的外層事務(wù)資源。即這些傳播行為都不會(huì)真正的開(kāi)啟數(shù)據(jù)庫(kù)級(jí)別的事務(wù)(不會(huì)獲取新的連接,對(duì)于普通事物來(lái)說(shuō))。
}
- 如果當(dāng)前已存在事務(wù),則handleExistingTransaction處理已存在事務(wù)
??
如果進(jìn)入事務(wù)切面之后,獲取到了已存在的連接并且開(kāi)啟了事務(wù)(通過(guò)isExistingTransaction方法判斷),那么將會(huì)執(zhí)行handleExistingTransaction方法執(zhí)行已存在事務(wù)時(shí)的邏輯,并返回一個(gè)TransactionStatus。該方法同樣將會(huì)根據(jù)此事務(wù)切面設(shè)置的事務(wù)傳播行為走不同的執(zhí)行流程,比如加入當(dāng)前事務(wù)、新建事務(wù)、拋出異常等等邏輯。
??大概邏輯是:
transaction found for transaction marked with propagation ‘never’”。
-
- 首先調(diào)用suspend方法掛起外層事務(wù),返回被掛起的資源。由于存在外層事務(wù),所以這里的參數(shù)就是獲取的外層事務(wù)參數(shù)。
-
- 隨后調(diào)用prepareTransactionStatus方法返回一個(gè)新的TransactionStatus,并在適當(dāng)時(shí)初始化事務(wù)同步。同樣,該方法和上面的startTransaction方法相比,其內(nèi)部會(huì)調(diào)用newTransactionStatus和prepareSynchronization,但不會(huì)調(diào)用doBegin方法,因此不會(huì)真正的開(kāi)啟事物。這里它的newTransaction參數(shù)為false,transaction參數(shù)為null,suspendedResources參數(shù)為被掛起的外層事務(wù)資源。
-
- 首先調(diào)用suspend方法掛起外層事務(wù),返回被掛起的資源。由于存在外層事務(wù),所以這里的參數(shù)就是獲取的外層事務(wù)參數(shù)。
-
- 隨后調(diào)用startTransaction方法真正的開(kāi)啟一個(gè)數(shù)據(jù)庫(kù)級(jí)別的事務(wù)(將會(huì)獲取新的連接開(kāi)啟一個(gè)新事物,舊的連接和事務(wù)則在上面的suspend方法中被掛起保存)。
如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)新“事務(wù)”作為當(dāng)前事務(wù)的嵌套事務(wù)來(lái)運(yùn)行;如果當(dāng)前沒(méi)有事務(wù),則等價(jià)于PROPAGATION_REQUIRED,即會(huì)新建一個(gè)事務(wù)運(yùn)行。
-
- 調(diào)用isNestedTransactionAllowed方法判斷是否允許PROPAGATION_NESTED行為,默認(rèn)不允許,但是DataSourceTransactionManager重寫為允許。不允許就拋出異常:“Transaction manager does not allow nested transactions……”。
-
- 調(diào)用useSavepointForNestedTransaction方法判斷是否對(duì)“嵌套事務(wù)”使用保存點(diǎn)Savepoint來(lái)實(shí)現(xiàn):
-
-
- 如果允許,那么首先調(diào)用prepareTransactionStatus方法返回一個(gè)新的TransactionStatus,并在適當(dāng)時(shí)初始化事務(wù)同步。這里它的newTransaction參數(shù)為false,transaction參數(shù)為外層事務(wù),suspendedResources參數(shù)為null,newSynchronization參數(shù)為false。隨后調(diào)用createAndHoldSavepoint創(chuàng)建保存點(diǎn),將使用數(shù)據(jù)庫(kù)的保存點(diǎn)的特性來(lái)實(shí)現(xiàn)“嵌套事務(wù)”,這是用語(yǔ)大部分普通事務(wù)。
-
-
-
- 否則將調(diào)用startTransaction方法通過(guò)在外層事務(wù)中嵌套的begin和commit/rollback調(diào)用來(lái)開(kāi)啟真正的嵌套事務(wù),不過(guò)通常僅用于JTA:如果存在預(yù)先存在的JTA事務(wù),則可以在此處激活Spring同步。
-
-
- 那么這里同樣調(diào)用prepareTransactionStatus方法,這里它的newTransaction參數(shù)為false,transaction參數(shù)為外層事務(wù),suspendedResources參數(shù)為null。
從源碼中我們能夠看到,如果傳播行為是PROPAGATION_NESTED:
- createAndHoldSavepoint創(chuàng)建保存點(diǎn)
該方法很簡(jiǎn)單,最終會(huì)調(diào)用當(dāng)前的JDBC連接Connection的setSavepoint方法創(chuàng)建一個(gè)保存點(diǎn),并且被設(shè)置給當(dāng)前TransactionStatus對(duì)象的savepoint屬性。
/*** AbstractTransactionStatus的方法* <p>* 創(chuàng)建一個(gè)保存點(diǎn)并將其保存在事務(wù)中。** @throws NestedTransactionNotSupportedException 如果基礎(chǔ)事務(wù)不支持保存點(diǎn)*/ public void createAndHoldSavepoint() throws TransactionException {setSavepoint(getSavepointManager().createSavepoint()); } /*** AbstractTransactionStatus的屬性*/ @Nullable private Object savepoint;/*** AbstractTransactionStatus的方法* <p>* 設(shè)置此事務(wù)的保存點(diǎn),對(duì)PROPAGATION_NESTED有用。*/ protected void setSavepoint(@Nullable Object savepoint) {this.savepoint = savepoint; }getSavepointManager獲取獲取保存點(diǎn)管理器,實(shí)際上創(chuàng)建的內(nèi)部事務(wù)對(duì)象都是SavepointManager接口的實(shí)現(xiàn),具有獲取保存點(diǎn)的方法!因此返回的實(shí)際上就是內(nèi)部的事務(wù)對(duì)象,對(duì)于DataSourceTransactionManager來(lái)說(shuō)創(chuàng)建的內(nèi)部事務(wù)就是DataSourceTransactionObject。
/*** DefaultTransactionStatus的屬性* <p>* 獲取基礎(chǔ)事務(wù)對(duì)象的SavepointManager,實(shí)際上就是獲取的內(nèi)部事務(wù)對(duì)象*/ @Nullable private final Object transaction;/*** DefaultTransactionStatus的方法* <p>* 獲取基礎(chǔ)事務(wù)對(duì)象的SavepointManager,實(shí)際上就是獲取的內(nèi)部事務(wù)對(duì)象*/ @Override protected SavepointManager getSavepointManager() {//獲取內(nèi)部事務(wù),對(duì)于DataSourceTransactionManager來(lái)說(shuō)創(chuàng)建的內(nèi)部事務(wù)就是DataSourceTransactionObjectObject transaction = this.transaction;if (!(transaction instanceof SavepointManager)) {throw new NestedTransactionNotSupportedException("Transaction object [" + this.transaction + "] does not support savepoints");}return (SavepointManager) transaction; }createSavepoint用于從當(dāng)前保存點(diǎn)管理器(事務(wù)對(duì)象)中創(chuàng)建一個(gè)保存點(diǎn),實(shí)際上就是獲取事務(wù)對(duì)象里面的ConnectionHolder,然后在獲取ConnectionHolder里面的Connection,最后調(diào)用Connection.setSavepoint方法創(chuàng)建并獲取保存點(diǎn)。
/*** DataSourceTransactionObject的父類JdbcTransactionObjectSupport的方法* <p>* 創(chuàng)建一個(gè)JDBC 3.0保存點(diǎn)并返回它。*/ @Override public Object createSavepoint() throws TransactionException {//校驗(yàn)規(guī)則并獲取,此前創(chuàng)建的ConnectionHolder,其內(nèi)部保存了獲取的連接ConnectionConnectionHolder conHolder = getConnectionHolderForSavepoint();try {//如果不允許保存點(diǎn),那么拋出異常,默認(rèn)允許if (!conHolder.supportsSavepoints()) {throw new NestedTransactionNotSupportedException("Cannot create a nested transaction because savepoints are not supported by your JDBC driver");}//如果被設(shè)置為僅回滾,那么拋出異常if (conHolder.isRollbackOnly()) {throw new CannotCreateTransactionException("Cannot create savepoint for transaction which is already marked as rollback-only");}return conHolder.createSavepoint();} catch (SQLException ex) {throw new CannotCreateTransactionException("Could not create JDBC savepoint", ex);} }/*** DataSourceTransactionObject的父類JdbcTransactionObjectSupport的方法* <p>* 為了創(chuàng)建一個(gè)JDBC 3.0保存而獲取ConnectionHolder。*/ protected ConnectionHolder getConnectionHolderForSavepoint() throws TransactionException {//如果不允許保存點(diǎn),那么拋出異常,默認(rèn)允許if (!isSavepointAllowed()) {throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions");}//如果沒(méi)有ConnectionHolder,那么拋出異常,默認(rèn)允許if (!hasConnectionHolder()) {throw new TransactionUsageException("Cannot create nested transaction when not exposing a JDBC transaction");}//返回此前創(chuàng)建的ConnectionHolder,其內(nèi)部保存了獲取的連接return getConnectionHolder(); }//ConnectionHolder的方法的屬性/*** 保存點(diǎn)名稱的前綴。*/ public static final String SAVEPOINT_NAME_PREFIX = "SAVEPOINT_";/*** 從此連接中獲取的保存點(diǎn)的數(shù)量*/ private int savepointCounter = 0;/*** ConnectionHolder的方法* 為當(dāng)前連接創(chuàng)建一個(gè)新的JDBC 3.0保存點(diǎn),只用SAVEPOINT_+savepointCounter作為保存點(diǎn)的名稱** @return 新的保存點(diǎn)* @throws SQLException if thrown by the JDBC driver*/ public Savepoint createSavepoint() throws SQLException {//獲取數(shù)量自增1this.savepointCounter++;//獲取內(nèi)部的JDBC連接,并通過(guò)連接設(shè)置一個(gè)保存點(diǎn),返回創(chuàng)建的Savepointreturn getConnection().setSavepoint(SAVEPOINT_NAME_PREFIX + this.savepointCounter); }四、小結(jié)
createTransactionIfNecessary方法創(chuàng)建并返回一個(gè)TransactionInfo對(duì)象,并且在此過(guò)程中,將會(huì)調(diào)用getTransaction方法獲取事務(wù)TransactionStatus。
getTransaction方法就是Spring事務(wù)處理的核心方法之一,該方法根據(jù)配置的各種事務(wù)傳播行為以及是否存在外層事務(wù)做出不同的處理,方法執(zhí)行完畢將可能開(kāi)啟了新事物,也可能沒(méi)有開(kāi)啟,甚至拋出異常。
TransactionInfo內(nèi)部保存了事務(wù)管理器transactionManager、事務(wù)屬性transactionAttribute、全路徑方法名joinpointIdentification。還保存了當(dāng)前方法的事務(wù)transactionStatus,以及前一個(gè)方法的事務(wù)信息對(duì)象oldTransactionInfo。
TransactionStatus實(shí)際類型為DefaultTransactionStatus,它持有一個(gè)transaction內(nèi)部事務(wù)對(duì)象、被掛起的事務(wù)資源以及一些事務(wù)的屬性,這個(gè)內(nèi)部事務(wù)對(duì)象由事務(wù)管理器的實(shí)現(xiàn)各自創(chuàng)建,不同的事務(wù)管理器將會(huì)創(chuàng)建不同的類型,因此使用Object來(lái)表示,對(duì)于DataSourceTransactionManager來(lái)說(shuō),它創(chuàng)建的事務(wù)對(duì)象就是DataSourceTransactionObject。
DataSourceTransactionObject內(nèi)部持有一個(gè)ConnectionHolder對(duì)象以及一些事務(wù)的屬性,ConnectionHolder對(duì)象內(nèi)部持有一個(gè)為了配置數(shù)據(jù)庫(kù)事務(wù)而獲取的JDBC連接Connection,以及是否開(kāi)啟了事務(wù)的標(biāo)志transactionActive,以及其他屬性,比如保存點(diǎn)計(jì)數(shù)。
最終,我們?yōu)槟硞€(gè)方法定義的事務(wù)屬性,除了傳播行為之外(Spring提供的特性并自行處理),都會(huì)反應(yīng)到Connection的對(duì)應(yīng)操作上,比如隔離級(jí)別、超時(shí)時(shí)間,是否只讀等等,關(guān)鍵方法就是doBegin,該方法用于真正的開(kāi)啟數(shù)據(jù)庫(kù)層面的事務(wù)。
我們用了很長(zhǎng)的文章講解了createTransactionIfNecessary方法的邏輯和源碼,這是Spring 事務(wù)開(kāi)啟的核心處理方法(可能并未真正的開(kāi)啟事務(wù)),剩下的方法比如proceedWithInvocation、completeTransactionAfterThrowing、cleanupTransactionInfo、commitTransactionAfterReturning就是在開(kāi)啟事物之后的處理方法,比如回滾、提交、恢復(fù)事務(wù)等等。
參考文章
總結(jié)
以上是生活随笔為你收集整理的Spring TX源码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2020年中国互联网租车报告
- 下一篇: SpringMVC 参数解析器