1. 程式人生 > >Spring之執行事務流程

Spring之執行事務流程

上一篇文章我們已經對配置檔案進行了解析,對bean的例項化過程也和之前的差不多,就不再分析了,我們這次主要的任務就是看一下事務是怎麼在Spring中執行的:

userService.save();
userService.update();

這兩行程式碼是展開本文的入口:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		MethodInvocation invocation;
		...
	}

由於我們在配置事務AOP的時候,讓他掃描了service所在的包,所以,在建立UserService 的時候新增到IOC容器中的是它的 代理類,所以在執行它的save方法的時候直接進入了invoke方法:

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

獲得針對這個方法的所有攔截器

[[email protected]74bada02, 
org[email protected]525575]

發現有兩個,一個是自動新增的攔截器,另外一個就是有關事務的攔截器了

invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);

將這個代理類的一些資訊封裝成MethodInvocation

retVal = invocation.proceed();

開始從攔截器鏈的頭部開始執行,我們直接跳到事務的那個攔截器的處理邏輯上:

public Object invoke(final MethodInvocation invocation) throws Throwable {
		// 獲得目標類的class物件
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}

這個方法首先獲得了目標類的class物件,然後開始執行invokeWithinTransaction,由於這個方法比較長,像以前一樣,我們一部分一部分的執行:

//獲得事務的封裝物件
TransactionAttributeSource tas = getTransactionAttributeSource();
//獲得當前方法的事務屬性
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
//獲得事務管理類,這個主要是根據我們選擇的資料來源有關的
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
//獲得方法完整的限定名
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

這段程式碼針對當前方法獲得了事務的屬性,方法的限定名,和事務管理類

TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

根據給定的TransactionAttribute建立一個事務,下面是這個方法完整的程式碼:

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

		// 如果沒有指定名稱,則應用方法標識作為事務名稱。
		if (txAttr != null && txAttr.getName() == null) {
			//封裝事務的屬性
			txAttr = new DelegatingTransactionAttribute(txAttr) {
				@Override
				public String getName() {
					return joinpointIdentification;
				}
			};
		}

		TransactionStatus status = null;
		if (txAttr != null) {
			if (tm != null) {
				//獲得當前事務的狀態
				status = tm.getTransaction(txAttr);
			}
			...
		}
		return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}

我們繼續看一下TransactionStatus 物件是怎麼獲取到的:

Object transaction = doGetTransaction();

首先執行了這個方法:

protected Object doGetTransaction() {
		//獲取資料來源的事務物件
		DataSourceTransactionObject txObject = new DataSourceTransactionObject();
		//設定回滾點
		txObject.setSavepointAllowed(isNestedTransactionAllowed());
		//獲得封裝連線的物件
		ConnectionHolder conHolder =
				(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
		//填充屬性
		txObject.setConnectionHolder(conHolder, false);
		return txObject;
	}

這個方法建立了一個數據源的事務物件

//是否需要同步
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
//封裝DefaultTransactionStatus
DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
//開始執行事務
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;

我們終於看到了要開始執行事務了,走進doBegin看一下:

protected void doBegin(Object transaction, TransactionDefinition definition) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		Connection con = null;

		try {
			if (!txObject.hasConnectionHolder() ||
					txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
				//獲得資料來源的連線
				Connection newCon = obtainDataSource().getConnection();
				if (logger.isDebugEnabled()) {
					logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
				}
				//將連線設定到事務物件中
				txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
			}
			//設定同步事務
			txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
			//獲得連線
			con = txObject.getConnectionHolder().getConnection();
			//獲得隔離級別
			Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
			txObject.setPreviousIsolationLevel(previousIsolationLevel);

			// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
			// so we don't want to do it unnecessarily (for example if we've explicitly
			// configured the connection pool to set it already).
			if (con.getAutoCommit()) {
				txObject.setMustRestoreAutoCommit(true);
				if (logger.isDebugEnabled()) {
					logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
				}
				//禁止自動提交,開啟事務
				con.setAutoCommit(false);
			}
			//事務開始後立即準備事務
			prepareTransactionalConnection(con, definition);
			//設定事務已經開始
			txObject.getConnectionHolder().setTransactionActive(true);

			int timeout = determineTimeout(definition);
			if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
				txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
			}

			// 將連線繫結到執行緒。
			if (txObject.isNewConnectionHolder()) {
				TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
			}
		}

		catch (Throwable ex) {
			if (txObject.isNewConnectionHolder()) {
				DataSourceUtils.releaseConnection(con, obtainDataSource());
				txObject.setConnectionHolder(null, false);
			}
			throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
		}
	}

在這個方法中設定了事務的屬性並開啟了事務

protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, String joinpointIdentification,
			@Nullable TransactionStatus status) {
		//例項化一個新的當前事務的資訊
		TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
		if (txAttr != null) {			
			//添加當前事務的狀態
			txInfo.newTransactionStatus(status);
		}
	

		// 綁定當前執行緒
		txInfo.bindToThread();
		return txInfo;
	}

建立了當前事務的資訊,最終返回到了這個方法中invokeWithinTransaction,接下來的程式都是執行在事務開啟環境當中的:

retVal = invocation.proceedWithInvocation();

進入這個方法後由於攔截器已經執行完了,所以下面執行的是目標方法

invokeJoinpoint();

新增事務後,我們發現其實和以前講AOP的流程沒有什麼差別,無非是在事務的攔截器中開啟了事務,目標方法結束後提交了事務。