1. 程式人生 > >Spring事務處理的實現:事務處理配置的讀入以及事務處理攔截器的實現

Spring事務處理的實現:事務處理配置的讀入以及事務處理攔截器的實現

事務處理配置的讀入

上次說到AOP配置已經完成,下面我們來講述一下關於具體的事務屬性配置是如何讀入的。書上所述為在TransactionProxyFactoryBean中,以TransactionAttributeSourceAdvisor的實現為入口,瞭解具體的事務屬性配置是如何被讀入的。

這個是我們已經見過的攔截器

private TransactionInterceptor transactionInterceptor;

這個是關於一個切面的配置

private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
		@Override
		protected TransactionAttributeSource getTransactionAttributeSource() {
			return (transactionInterceptor != null ? transactionInterceptor.getTransactionAttributeSource() : null);
		}
	};

在宣告式事務處理中,是通過對目標物件的方法呼叫進行攔截實現的。這和我們的AOP實現機制是非常相似的,通過對代理物件的方法執行,實現了對目標物件的方法增強。對於攔截的啟動,首先要對方法呼叫是否需要攔截進行判斷,判斷的依據就是在TransactionProxyFactoryBean中為目標物件設定的事務屬性。那麼這個需要進行事務攔截的方法就由我們TransactionAttributeSourcePointcut完成並持有。

public boolean matches(Method method, Class<?> targetClass) {
		if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
			return false;
		}
		TransactionAttributeSource tas = getTransactionAttributeSource();
		return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
	}

同樣在這個類中我們可以看到matches方法,具體的實現原理和之前的AOP中Pointcut是類似的。它首先會把事務方法的屬性配置讀取到TransactionAttributeSource中,有了這些事務的配置。我們在執行方法的時候就可以判斷它是否需要加強和進行怎樣的加強。這個方法裡面需要呼叫TransactionAttributeSource 物件,這個物件用的是NameMatchTransactionAttributeSource物件,

通過這個方法我們可以看出,這個方法是在我們的TransactionProxyFactoryBean中配置TransactionInterceptor物件時呼叫的

public void setTransactionAttributes(Properties transactionAttributes) {
		NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
		tas.setProperties(transactionAttributes);
		this.transactionAttributeSource = tas;
	}

那麼就是在這個NameMatchTransactionAttributeSource中實現的事務處理屬性的讀入和匹配的。首先是對於事務方法名和配置屬性的設定

public void setProperties(Properties transactionAttributes) {
		TransactionAttributeEditor tae = new TransactionAttributeEditor();
		Enumeration<?> propNames = transactionAttributes.propertyNames();
		while (propNames.hasMoreElements()) {
			String methodName = (String) propNames.nextElement();
			String value = transactionAttributes.getProperty(methodName);
			tae.setAsText(value);
			TransactionAttribute attr = (TransactionAttribute) tae.getValue();
			addTransactionalMethod(methodName, attr);
		}
	}
public void addTransactionalMethod(String methodName, TransactionAttribute attr) {
		if (logger.isDebugEnabled()) {
			logger.debug("Adding transactional method [" + methodName + "] with attribute [" + attr + "]");
		}
		this.nameMap.put(methodName, attr);
	}

可以看到這裡是通過一個map來持有相應的方法名和對應的事務屬性。之前我們的matches方法中呼叫了getTransactionAttribute(Method, Class<?>)方法,那麼我們來看一下這個方法是如何將相應的事務屬性進行返回的。

首先是根據方法名從之前我們配置的map中進行查詢,如果查詢不到相應的name。那麼就根據命名模式進行匹配,因為有的方法描述帶有萬用字元*,所以需要用匹配萬用字元的方法來匹配。

	String methodName = method.getName();
		TransactionAttribute attr = this.nameMap.get(methodName);

		if (attr == null) {
			// Look for most specific name match.
			String bestNameMatch = null;
			for (String mappedName : this.nameMap.keySet()) {
				if (isMatch(methodName, mappedName) &&
						(bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
					attr = this.nameMap.get(mappedName);
					bestNameMatch = mappedName;
				}
			}
		}

事務處理攔截器的實現

我們知道,我們的目標方法的增強是通過我們生成的代理物件的invoke方法實現的,在我們的TransactionInterceptor的Invoke方法中,首先需要得到事務處理配置,會取得配置的PlatformTransactionManager事務處理器。這個類我想大家應該比較熟悉,在我們學習Spring 事務的時候,就用到了這個類。

	public Object invoke(final MethodInvocation invocation) throws Throwable {
		// Work out the target class: may be {@code null}.
		// The TransactionAttributeSource should be passed the target class
		// as well as the method, which may be from an interface.
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		// Adapt to TransactionAspectSupport's invokeWithinTransaction...
		return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
			@Override
			public Object proceedWithInvocation() throws Throwable {
				return invocation.proceed();
			}
		});
	}

進入其中的invokeWithinTransaction方法

首先需要讀取事務配置屬性

final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);

之後我們需要取得具體的事務處理器

final PlatformTransactionManager tm = determineTransactionManager(txAttr);

建立事務,將事務的狀態資訊交由TransactionInfo物件來儲存。

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

之後就需要沿著我們之前宣告好的攔截器鏈進行,最終呼叫我們的方法。如果出現異常要對異常進行處理,最後把與執行緒繫結的

TransactionInfo 設定為oldTransactionInfo。最後通過事務處理器來對事務進行提交。這個方法的實現將會是我下一次部落格的主要內容。

commitTransactionAfterReturning(txInfo);

書上所說還有一種事務的使用方式是通過回撥來實現的。這裡我就不再提及,因為它和我們接下來要說的方法關係不大。可以在原書中進行查詢。