Spring源碼分析(八)深刻了解事務管理的流程

1、前言

事務管理對於系統應用來講相當重要,它保證了數據的完整性和安全性。特別是針對金融服務而言,更是不可或缺。經典的場景是轉帳操做,A帳戶向B帳戶轉帳5000元,首先A餘額減小5000元,而後B餘額增長5000元。一般狀況下,都能正常完成交易。但也不免會遇到故障,這時候不能出現A的餘額減小了,B的餘額卻沒有增長的狀況。java

在分析源碼以前,咱們先來了解下Spring中的一些事務屬性。mysql

2、事務屬性

一、事務隔離級別

事務隔離級別,定義了一個事務可能受其餘併發事務活動影響的程度。spring

在應用程序中,多個事務同時運行,常常會爲了完成相同的工做而操做同一數據。併發是必然的,但可能會致使如下問題。sql

  • 髒讀(Dirty read)。有T一、T2兩個事務,T1改寫了數據但還沒有提交,T2卻能夠讀取到改寫後的數據。數據庫

  • 不可重複讀(Nonrepeatable read)。有T一、T2兩個事務。T1屢次執行相同的查詢,但獲得的結果卻不相同。一般是由於T2在T1查詢期間對數據作了更新。express

  • 幻讀(Phantom reads)。有T一、T2兩個事務。當T1正在讀取記錄時,T2併發插入了記錄,幻讀發生了。其實跟不可重複讀相似。apache

理想狀態下,全部的事務都應該隔離,從而防止以上狀況出現。然而,徹底將事務隔離,將大大下降性能。由於隔離要鎖定數據行或者數據表,會阻礙併發,要求事務相互等待來完成工做。因此,就區分了幾種隔離級別,來靈活應對不一樣場景下的數據要求。編程

隔離級別 含義
DEFAULT 這是Spring中的事務隔離級別默認值。它表明使用底層數據庫的默認隔離級別。MySQL默認是「可重複讀」,Oracle默認是「提交讀」。
READ_UNCOMMITTED 未提交讀,容許讀取到還沒有提交的數據。會致使髒讀、不可重複讀和幻讀。
READ_COMMITTED 已提交讀,容許讀取到已經提交的事務數據。能夠防止髒讀,但仍會出現不可重複讀和幻讀。
REPEATABLE READ 可重複讀,對相同字段的屢次讀取的結果是一致的,除非數據被當前事務自己改變。能夠避免髒讀和不可重複讀,但幻讀仍會發生。
SERIALIZABLE 串行化,全部的事務依次逐個執行,這樣事務之間就徹底不可能產生干擾。事實上,基本不會使用到這個級別。

二、事務傳播行爲

所謂事務的傳播行爲是指,若是在開始當前事務以前,一個事務上下文已經存在,此時有若干選項能夠指定一個事務性方法的執行行爲。Spring中定義7種傳播行爲。後端

傳播行爲 含義
PROPAGATION_MANDATORY 表示該方法必須在一個事務中運行。若是當前沒有事務,則拋出異常。
PROPAGATION_NEVER 表示該方法不該當在一個事務中運行。若是一個事務正在運行,則拋出異常。
PROPAGATION_NOT_SUPPORTED 表示該方法不該該在一個事務中運行。若是一個現有事務正在進行中,它將在該方法的運行期間被掛起。
PROPAGATION_SUPPORTS 表示當前方法不須要事務性上下文,可是若是有一個事務已經在運行的話,它也能夠在這個事務裏運行。
PROPAGATION_REQUIRES_NEW 表示當前方法必須在它本身的事務裏運行。一個新的事務將被啓動,並且若是有一個現有事務在運行的話,則將在這個方法運行期間被掛起。
PROPAGATION_REQUIRES 表示當前方法必須在一個事務中運行。若是一個現有事務正在進行中,該方法將在那個事務中運行,不然就要開始一個新事務。
PROPAGATION_NESTED 若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行;若是當前沒有事務,則該取值等價於PROPAGATION_REQUIRED。

三、事務超時

所謂事務超時,就是指一個事務所容許執行的最長時間,若是超過該時間限制但事務尚未完成,則自動回滾事務。tomcat

四、只讀屬性

事務的只讀屬性是指,對事務性資源進行只讀操做或者是讀寫操做。若是一個事務只對後端數據庫執行讀操做,那麼該數據庫就可能利用那個事務的只讀特性,採起某些優化 措施。經過把一個事務聲明爲只讀,能夠給後端數據庫一個機會來應用那些它認爲合適的優化措施。

五、回滾規則

一般狀況下,若是在事務中拋出了未檢查異常(繼承自 RuntimeException 的異常),則默認將回滾事務。若是沒有拋出任何異常,或者拋出了已檢查異常,則仍然提交事務。這一般也是大多數開發者但願的處理方式,也是 EJB 中的默認處理方式。可是,咱們能夠根據須要人爲控制事務在拋出某些未檢查異常時任然提交事務,或者在拋出某些已檢查異常時回滾事務。

3、Spring事務的三大接口

一、 PlatformTransactionManager

PlatformTransactionManager是事務管理的抽象層,Spring根據這個抽象層提供許多不一樣的具體實現。好比DataSourceTransactionManager、JpaTransactionManager、HibernateTransactionManager等。

public interface PlatformTransactionManager {
	
	//返回當前活動的事務或建立一個新的事務。
	//參數definition描述了事務的屬性,好比傳播行爲,隔離級別,超時等
	TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException
	//根據給定事務的狀態提交給定事務
	void commit(TransactionStatus status) throws TransactionException;	
	//執行給定事務的回滾
	void rollback(TransactionStatus status) throws TransactionException;
}
複製代碼

二、 TransactionDefinition

定義了事務屬性

public interface TransactionDefinition {
	
	//事務的7個傳播行爲
	int PROPAGATION_REQUIRED = 0;
	int PROPAGATION_SUPPORTS = 1;
	int PROPAGATION_MANDATORY = 2;
	int PROPAGATION_REQUIRES_NEW = 3;
	int PROPAGATION_NOT_SUPPORTED = 4;
	int PROPAGATION_NEVER = 5;
	int PROPAGATION_NESTED = 6;

	//事務的5個隔離級別
	int ISOLATION_DEFAULT = -1;
	int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
	int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
	int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
	int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;

	//事務超時時間
	int TIMEOUT_DEFAULT = -1;
	//返回傳播行爲
	int getPropagationBehavior();
	//返回隔離級別
	int getIsolationLevel();
	//返回超時時間
	int getTimeout();
	//是否爲只讀事務
	boolean isReadOnly();
	//返回事務的名稱
	String getName();
}
複製代碼

三、 TransactionStatus

表明當前事務的狀態,也能夠對當前事務進行控制。

public interface TransactionStatus extends SavepointManager, Flushable {

	//當前事務狀態是不是新事務
	boolean isNewTransaction();

	//當前事務是否有保存點
	boolean hasSavepoint();

	//設置當前事務應該回滾,若是設置這個,則commit不起做用
	void setRollbackOnly();

	//當前事務是否應該回滾
	boolean isRollbackOnly();

	//用於刷新底層會話中的修改到數據庫,通常用於刷新如Hibernate/JPA的會話,
   //可能對如JDBC類型的事務無任何影響
	void flush();

	//當前事務否已經完成
	boolean isCompleted();

}
複製代碼

4、事務的實現方式

Spring中的事務實現方式,整體能夠分爲兩類:編程式事務和聲明式事務。

一、編程式事務

顧名思義,編程式事務就是以代碼編程的方式來控制事務的運行。咱們來看一個事例。 PlatformTransactionManager採用Spring JDBC的事務管理器。數據源是一個數據庫鏈接池,先看下XML配置。

<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">  
	<property name="driverClassName" value="${jdbc.driverClassName}" />  
	<property name="url" value="${jdbc.url}" />  
	<property name="username" value="${jdbc.username}" />  
	<property name="password" value="${jdbc.password}" />  
</bean>
 
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
	<property name="dataSource" ref="dataSource" />  
</bean>  

<bean id="transactionDefinition" class="org.springframework.transaction.support.DefaultTransactionDefinition">
	<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
</bean>
複製代碼

在代碼中直接經過事務管理器便可控制事務的運行。咱們看一個Service中的方法。

public void insertUser(User user) {
	TransactionStatus txStatus = transactionManager.getTransaction(transactionDefinition);
	try {
		System.out.println("----------新增用戶信息------------");
		transactionManager.commit(txStatus);
	} catch (Exception e) {
		System.out.println("保存用戶信息發送異常,"+e);
		transactionManager.rollback(txStatus);
	}
}
複製代碼

經過以上方式就完成了以編程式事務對業務方法的管理。固然了,它的缺點也很明顯,事務代碼和業務代碼糅雜在一塊兒,破壞了業務代碼條理性,並且也不利於維護和擴展。 有沒有更好的實現方法呢?結合咱們上一節學習的Spring AOP知識,應該怎麼作呢?

二、聲明式事務

沒錯,利用Spring AOP對方法進行攔截。在方法開始以前建立或者加入一個事務,在方法執行完畢以後根據狀況提交或回滾事務。這個就是聲明式事務。咱們來看一個基於 命名空間的聲明式事務管理。

首先,仍是先配置一個事務管理器。

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
	<property name="dataSource" ref="dataSource" />  
</bean>  
複製代碼

其次,經過AOP標籤配置一個advisor,它包含一個advice和一個pointcut。

<aop:config>  
	<aop:pointcut id="serviceMethods" 
		expression="execution(* com.viewscenes.netsupervisor.service..*(..))"/>  
	<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>  
</aop:config>  
複製代碼

最後,經過tx標籤訂義一個advice。它自己是一個事務的通知,當前要包含事務的管理器和事務的屬性。

<tx:advice id="txAdvice" transaction-manager="transactionManager">  
	<tx:attributes>  
		<tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>  
		<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>  
		<tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>  
		<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>  
		<tx:method name="*"/>  
	</tx:attributes>  
</tx:advice>  
複製代碼

經過這種方式,咱們的業務代碼不須要添加任何關於事務的代碼,就能夠完成事務的操做。在實際開發中,咱們大部分也都是使用這種方式或者經過Annotation的方式來配置事務,而不大可能使用編程式事務。

5、源碼解析

囉嗦了這麼多,是爲了先把事務的運行規則、屬性講清楚,否則上來就是源碼,容易暈車哈。源碼以tx標籤爲例的配置方式進行分析,編程式事務和Annotation註解方式的事務本章節暫不涉及。

一、tx標籤的解析

tx標籤的解析,在Spring掃描XML的時候就被加載到了,具體會定位到org.springframework.transaction.config.TxAdviceBeanDefinitionParser類。而後調用類的parse()方法,可是咱們發現此類並無parse方法,往上找最後調用到父類的父類AbstractBeanDefinitionParser.parse()。最後返回一個構建完畢的BeanDefinition對象,並註冊到bean容器中,等待下一步的實例化。整個過程咱們能夠分爲三個步驟來看。

  • 一、建立TransactionInterceptor

TransactionInterceptor類就是事務處理的攔截器類,它實現了MethodInterceptor接口。在調用代理類的invoke方法時,實際調用的就是這個類的invoke。

protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
	
	BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
	
	//getBeanClass調用到子類的方法,這個子類就是TxAdviceBeanDefinitionParser
	//它返回的就是return TransactionInterceptor.class;
	Class<?> beanClass = getBeanClass(element);
	if (beanClass != null) {
		builder.getRawBeanDefinition().setBeanClass(beanClass);
	}
	builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
	//調用子類TxAdviceBeanDefinitionParser方法
	doParse(element, parserContext, builder);
	return builder.getBeanDefinition();
}
複製代碼
  • 二、解析事務屬性

第一步建立了TransactionInterceptor的BeanDefinition對象,而後調用子類的doParse方法解析子節點進行事務屬性的添加。

protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
	
	//將配置的事務管理器添加到屬性
	builder.addPropertyReference("transactionManager", TxNamespaceHandler.getTransactionManagerName(element));
	//獲取tx的子標籤<tx:attributes> 。這裏明確規定了只能有一個子標籤
	List<Element> txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES_ELEMENT);
	if (txAttributes.size() > 1) {
		parserContext.getReaderContext().error(
				"Element <attributes> is allowed at most once inside element <advice>", element);
	}
	else if (txAttributes.size() == 1) {
		Element attributeSourceElement = txAttributes.get(0);
		//解析事務方法的屬性。也就是標籤<tx:method name="insert*"/>
		RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);
		
		//將NameMatchTransactionAttributeSource類的實例添加到屬性中。
		//NameMatchTransactionAttributeSource是一個包含了全部的事務屬性的實例
		builder.addPropertyValue("transactionAttributeSource", attributeSourceDefinition);
	}
	else {
		builder.addPropertyValue("transactionAttributeSource",
				new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"));
	}
}

private RootBeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {
	//獲取全部的子標籤tx:method
	//好比<tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>  
	List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT);
	
	//事務屬性的Map
	ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap =
		new ManagedMap<TypedStringValue, RuleBasedTransactionAttribute>(methods.size());
	transactionAttributeMap.setSource(parserContext.extractSource(attrEle));

	//將method標籤裏面的name爲key,其餘的事務屬性爲value加入Map
	for (Element methodEle : methods) {
		String name = methodEle.getAttribute(METHOD_NAME_ATTRIBUTE);
		TypedStringValue nameHolder = new TypedStringValue(name);
		nameHolder.setSource(parserContext.extractSource(methodEle));

		RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();
		String propagation = methodEle.getAttribute(PROPAGATION_ATTRIBUTE);
		String isolation = methodEle.getAttribute(ISOLATION_ATTRIBUTE);
		String timeout = methodEle.getAttribute(TIMEOUT_ATTRIBUTE);
		String readOnly = methodEle.getAttribute(READ_ONLY_ATTRIBUTE);
		if (StringUtils.hasText(propagation)) {
			attribute.setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION + propagation);
		}
		if (StringUtils.hasText(isolation)) {
			attribute.setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION + isolation);
		}
		if (StringUtils.hasText(timeout)) {
			try {
				attribute.setTimeout(Integer.parseInt(timeout));
			}
			catch (NumberFormatException ex) {
				parserContext.getReaderContext().error("Timeout must be an integer value: [" + timeout + "]", methodEle);
			}
		}
		if (StringUtils.hasText(readOnly)) {
			attribute.setReadOnly(Boolean.valueOf(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));
		}

		List<RollbackRuleAttribute> rollbackRules = new LinkedList<RollbackRuleAttribute>();
		if (methodEle.hasAttribute(ROLLBACK_FOR_ATTRIBUTE)) {
			String rollbackForValue = methodEle.getAttribute(ROLLBACK_FOR_ATTRIBUTE);
			addRollbackRuleAttributesTo(rollbackRules,rollbackForValue);
		}
		if (methodEle.hasAttribute(NO_ROLLBACK_FOR_ATTRIBUTE)) {
			String noRollbackForValue = methodEle.getAttribute(NO_ROLLBACK_FOR_ATTRIBUTE);
			addNoRollbackRuleAttributesTo(rollbackRules,noRollbackForValue);
		}
		attribute.setRollbackRules(rollbackRules);

		transactionAttributeMap.put(nameHolder, attribute);
	}
	//建立NameMatchTransactionAttributeSource類,將上面的事務屬性map放入本身的屬性nameMap
	RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchTransactionAttributeSource.class);
	attributeSourceDefinition.setSource(parserContext.extractSource(attrEle));
	attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);
	return attributeSourceDefinition;
}
複製代碼

而後將TransactionInterceptor的對象返回,此時這個對象的屬性集合中已經包含了事務管理器和全部的事務屬性。

  • 三、註冊TransactionInterceptor

將這個類註冊到bean容器。它的名字就是XML中配置的txAdvice。

this.beanDefinitionNames.add(beanName);
this.beanDefinitionMap.put(beanName, beanDefinition);
複製代碼

二、advisor的解析

advisor的解析咱們在上一章節已經深刻分析了,這裏就不過多展開。總之,它最後返回一個DefaultBeanFactoryPointcutAdvisor實例的BeanDefinition對象,重點是它有一個屬性adviceBeanName就是上面已經註冊到容器的txAdvice。

剩下的就是AOP生成代理的流程,實際調用Service方法來到AopProxy的invoke方法。仍是以JDK動態代理爲例,在調用Service方法時候,調用到JdkDynamicAopProxy.invoke()。或許你們還有印象,它先獲取方法的攔截鏈,也就是通知方法的集合,而後鏈式調用它們的invoke。在這裏,通知只有一個,那就是org.springframework.transaction.interceptor.TransactionInterceptor

三、事務控制

TransactionInterceptor.invoke()就是實際處理事務的地方。先來看一下這個方法的全貌。

public class TransactionInterceptor{
	public Object invoke(final MethodInvocation invocation) throws Throwable {
		
		//目標類的Class對象
		//class com.viewscenes.netsupervisor.service.impl.UserServiceImpl
		Class<?> targetClass = AopUtils.getTargetClass(invocation.getThis());
		//事務處理
		return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
			//回調方法,就是目標類的方法調用
			public Object proceedWithInvocation() throws Throwable {
				return invocation.proceed();
			}
		});
	}
}
複製代碼

能夠看出,invokeWithinTransaction方法纔是重點。一樣的,咱們也先來看一下它的內部實現。

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, 
                  final InvocationCallback invocation)throws Throwable {

	//獲取事務的屬性(傳播行爲、隔離級別等)
	final TransactionAttribute txAttr = getTransactionAttributeSource().
									getTransactionAttribute(method, targetClass);
	//獲取事務管理器
	final PlatformTransactionManager tm = determineTransactionManager(txAttr);
	//執行的方法 
	//com.viewscenes.netsupervisor.service.impl.UserServiceImpl.insertUser
	final String joinpointIdentification = methodIdentification(method, targetClass);

	//這個if就是聲明式事務,else就是編程式事務,暫不涉及。
	if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
		// Standard transaction demarcation with getTransaction and commit/rollback calls.
		TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification)
		Object retVal = null;
		try {
			//回調方法,調用下一個攔截鏈。
			//但實際上只有這一個通知,因此會調用目標對象實際方法。
			retVal = invocation.proceedWithInvocation();
		}
		catch (Throwable ex) {
			//rollback 回滾事務
			completeTransactionAfterThrowing(txInfo, ex);
			throw ex;
		}
		finally {
			cleanupTransactionInfo(txInfo);
		}
		//commit 提交事務
		commitTransactionAfterReturning(txInfo);
		return retVal;
	}
}
複製代碼

看到上面的源碼,思路就比較清晰了。

  • 獲取事務管理器和事務屬性
  • 執行業務方法
  • 根據try、catch決定回滾仍是提交事務

因此這也解答了爲何業務方法裏不能catch異常,不然事務不會回滾。若是必定要catch異常而且保持事務,那麼在catch以後手動再throw一下異常也是能夠的。以下所示:

public void insertUser(User user) {	
	try {
		int i = 1/0;
		System.out.println("----------新增用戶信息------------");
	} catch (Exception e) {
		System.out.println("UserServiceImpl.insertUser()"+e.getMessage());
		throw new RuntimeException();	
	}
}
複製代碼

不過在thorw的時候還須要注意,Spring在回滾的時候還有個判斷。也就是說,只要這兩種異常纔會回滾。

public boolean rollbackOn(Throwable ex) {
	return (ex instanceof RuntimeException || ex instanceof Error);
}
複製代碼

那麼,除了這種手動throw的方式,有沒有其餘的呢?固然,還記不記得在TransactionStatus接口中有個方法setRollbackOnly。咱們能夠設置它,控制事務只能夠回滾而不能提交。即使走到commit方法也不要緊,它是有一個判斷的。

if (defStatus.isLocalRollbackOnly()) {
	if (defStatus.isDebug()) {
		logger.debug("Transactional code has requested rollback");
	}
	processRollback(defStatus);
	return;
}
複製代碼

在業務代碼中調用便可,就像這樣。也能夠保證事務會回滾。

public void insertUser(User user) {		
	try {
		int i = 1/0;
		System.out.println("----------新增用戶信息------------");
	} catch (Exception e) {
		TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
	}
}
複製代碼

看完了整個處理流程,咱們的目光再回到createTransactionIfNecessary方法。由於咱們想了解事務究竟是怎麼被建立的。

  • 從數據源中獲取一個事務管理器
package org.springframework.jdbc.datasource;
public class DataSourceTransactionManager{
	
	//建立一個數據源事務管理器,從數據源獲取一個底層數據庫鏈接
	//conHolder此時仍是爲空
	protected Object doGetTransaction() {
		DataSourceTransactionObject txObject = new DataSourceTransactionObject();
		txObject.setSavepointAllowed(isNestedTransactionAllowed());
		ConnectionHolder conHolder =
			(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
		txObject.setConnectionHolder(conHolder, false);
		return txObject;
	}
}
複製代碼
  • 判斷事務屬性

判斷事務屬性,是否超時、傳播行爲

//事務超時
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
		throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
	}
//說明此方法必須在一個事務中運行。但此時還未開啓事務,因此要拋出異常。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
	throw new IllegalTransactionStateException(
			"No existing transaction found for transaction marked with propagation 'mandatory'");
}
複製代碼
  • 開啓事務 事務是與底層數據庫鏈接綁定的。
protected void doBegin(Object transaction, TransactionDefinition definition) {
	DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
	Connection con = null;
	try {
		//從數據源中獲取一個鏈接,並放到事務管理器中
		//newCon就是ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@62e9f76b]]
		//這就說明一個事務對應一個數據庫鏈接
		Connection newCon = this.dataSource.getConnection();
		txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
		txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
		con = txObject.getConnectionHolder().getConnection();

		//設置事務提交方式爲手動提交
		if (con.getAutoCommit()) {
			txObject.setMustRestoreAutoCommit(true);
			con.setAutoCommit(false);
		}
		//設置事務的鏈接狀態
		txObject.getConnectionHolder().setTransactionActive(true);	
		//超時時間
		int timeout = determineTimeout(definition);
		if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
			txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
		}
		// 將會話綁定到線程 threadlocal
		if (txObject.isNewConnectionHolder()) {
			TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
		}
	}
}
複製代碼
  • 封裝事務對象

將事務管理器,事務屬性和執行方法封裝成TransactionInfo對象,並設置事務的狀態,綁定到當前線程,最後返回。

protected TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm,
		TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {
	
	//將事務管理器、事務屬性和方法封裝成TransactionInfo對象
	TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
	if (txAttr != null) {
		//設置事務的狀態
		txInfo.newTransactionStatus(status);
	}
	//將事務綁定到當前線程,即ThreadLocal。
	//由於在commit的時候,會判斷當前線程是否有事務存在,不然不會提交
	txInfo.bindToThread();
	return txInfo;
}
複製代碼

6、總結

總的來講,Spring的事務管理分爲編程式事務和聲明式事務。

  • 基於 TransactionDefinition、PlatformTransactionManager、TransactionStatus 編程式事務管理是 Spring 提供的最原始的方式,一般咱們不會這麼寫,可是瞭解這種方式對理解 Spring 事務管理的本質有很大做用。

  • 基於 和 命名空間的聲明式事務管理是目前推薦的方式,其最大特色是與 Spring AOP 結合緊密,能夠充分利用切點表達式的強大支持,使得管理事務更加靈活。

咱們說基於tx標籤的聲明式事務是與AOP緊密結合的產物,經過對方法的攔截,它實際處理的時候調用的其實仍是編程式事務裏的那幾個接口方法。

相關文章
相關標籤/搜索