1. 程式人生 > >Spring Transaction 5.1.3 原始碼分析 : AnnotationTransactionAttributeSource 解析註解式事務屬性

Spring Transaction 5.1.3 原始碼分析 : AnnotationTransactionAttributeSource 解析註解式事務屬性

概述

Spring支援使用註解指定服務方法的事務屬性。這些事務註解可以用在類上,也可以用在方法上,如下例子所示 。

Spring事務註解例子–應用在類級別

將事務註解標記到服務元件類級別,相當於為該服務元件的每個服務方法都應用了這個註解。

import org.springframework.transaction.annotation.Transactional;
// 這裡省略其他與本文主題無關的import

@Transactional
@Service
public class EmployeeService {
	// 當前服務元件的一個服務方法,
	// 因為該服務元件使用了註解@Transactional,所以該服務元件的每個服務方法都會應用該註解屬性
public long add(String name) { // ... } // 省略該服務元件的其他實現部分 // ... }

Spring事務註解例子–應用在方法級別

跟將事務註解放在類層面相比,將事務註解應用在方法級別,是更細粒度的一種事務註解方式。這個服務元件的不同服務方法可以採用自己不同的事務註解。

下面的例子演示了在同一服務元件中,不同的方法使用不同的事務註解定義。

import javax.transaction.Transactional;
import static javax.transaction.Transactional.
TxType.NOT_SUPPORTED; // 這裡省略其他與本文主題無關的import @Service public class StudentService { @Autowired StudentRepository repo; @Transactional public long add(String name, String studentNo, long classId) { // 省略具體實現 } @Transactional(NOT_SUPPORTED) public long findByStudentNo(
String studentNo) { // 省略具體實現 }

另外,Spring 支援三個不同的事務註解 :

  1. Spring 事務註解 org.springframework.transaction.annotation.Transactional
  2. JTA事務註解 javax.transaction.Transactional
  3. EJB 3 事務註解 javax.ejb.TransactionAttribute

因為歷史原因,存在上面三種事務註解方式,這一點這裡不過多解釋。但這裡需要提醒的是:

  1. 上面三種方式具體使用的方式不完全一樣,使用時要注意各自具體的使用方式;
  2. 上面三種方式的目的/語義是一致的。

以上事務註解對服務方法所要使用的事務從以下方面做了定義:

  • 事務傳播方式
  • 事務隔離級別
  • 事務超時時間
  • 是否只讀事務
  • 遇到哪些異常回滾事務
  • 遇到哪些異常不要回滾事務

請注意: 具體到每個註解,對以上屬性的支援有所不同,請參考相應註解的定義 。

當遇到以上三種註解的某一個時,Spring會使用AnnotationTransactionAttributeSource分析該事務註解,最終組織成一個TransactionAttribute供隨後使用。本文將基於原始碼解析該過程。

原始碼解析

AnnotationTransactionAttributeSource

package org.springframework.transaction.annotation;

import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

import org.springframework.lang.Nullable;
import org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
		implements Serializable {

	// JTA 1.2事務註解是否被使用
	private static final boolean jta12Present;

	// EJB 3 事務註解是否被使用
	private static final boolean ejb3Present;

	static {
		ClassLoader classLoader = AnnotationTransactionAttributeSource.class.getClassLoader();
		// 根據註解類的存在性判斷 JTA 1.2 事務註解是否被使用
		jta12Present = ClassUtils.isPresent("javax.transaction.Transactional", classLoader);
		// 根據註解類的存在性判斷 EJB 3 事務註解是否被使用
		ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute", classLoader);
	}

	// 指出僅僅處理public方法(基於代理的AOP情況下的典型做法),
	// 還是也處理protected/private方法(使用AspectJ類織入方式下的典型做法)
	private final boolean publicMethodsOnly;

	// 儲存用於分析事務註解的事務註解分析器
	private final Set<TransactionAnnotationParser> annotationParsers;


	// 建構函式, publicMethodsOnly 預設使用 true
	public AnnotationTransactionAttributeSource() {
		this(true);
	}

	// 建構函式
	public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
		this.publicMethodsOnly = publicMethodsOnly;
		// 下面這段邏輯主要是準備用於分析事務註解的各個分析器,放到屬性 annotationParsers 中
		// Spring事務註解分析器總是
		// 會被使用 : SpringTransactionAnnotationParser
		if (jta12Present || ejb3Present) {
			// 根據 JTA , EJB 事務註解類的存在性決定要不要新增相應的事務註解處理器
			// JTA 事務註解分析器 : JtaTransactionAnnotationParser
			// EJB 事務註解分析器 : Ejb3TransactionAnnotationParser
			this.annotationParsers = new LinkedHashSet<>(4);
			this.annotationParsers.add(new SpringTransactionAnnotationParser());
			if (jta12Present) {
				this.annotationParsers.add(new JtaTransactionAnnotationParser());
			}
			if (ejb3Present) {
				this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
			}
		}
		else {
			this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
		}
	}

	/**
	 * Create a custom AnnotationTransactionAttributeSource.
	 * 建立一個定製的 AnnotationTransactionAttributeSource ,使用給定的事務註解分析器(一個),
	 * publicMethodsOnly 預設使用 true,僅針對public方法工作
	 * @param annotationParser the TransactionAnnotationParser to use
	 */
	public AnnotationTransactionAttributeSource(TransactionAnnotationParser annotationParser) {
		this.publicMethodsOnly = true;
		Assert.notNull(annotationParser, "TransactionAnnotationParser must not be null");
		this.annotationParsers = Collections.singleton(annotationParser);
	}

	/**
	 * Create a custom AnnotationTransactionAttributeSource.
	 * 建立一個定製的 AnnotationTransactionAttributeSource ,使用給定的事務註解分析器(多個),
	 * publicMethodsOnly 預設使用 true,僅針對public方法工作
	 * @param annotationParsers the TransactionAnnotationParsers to use
	 */
	public AnnotationTransactionAttributeSource(TransactionAnnotationParser... annotationParsers) {
		this.publicMethodsOnly = true;
		Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified");
		this.annotationParsers = new LinkedHashSet<>(Arrays.asList(annotationParsers));
	}

	/**
	 * Create a custom AnnotationTransactionAttributeSource.
     * 建立一個定製的 AnnotationTransactionAttributeSource ,使用給定的事務註解分析器(多個),
	 * publicMethodsOnly 預設使用 true,僅針對public方法工作
	 * @param annotationParsers the TransactionAnnotationParsers to use
	 */
	public AnnotationTransactionAttributeSource(Set<TransactionAnnotationParser> annotationParsers) {
		this.publicMethodsOnly = true;
		Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified");
		this.annotationParsers = annotationParsers;
	}


	// 獲取某個類上的事務註解屬性
	@Override
	@Nullable
	protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
		// Class 實現了 AnnotatedElement 介面,所以交給下面的 
		// determineTransactionAttribute(AnnotatedElement)執行具體的分析邏輯
		return determineTransactionAttribute(clazz);
	}

	// 獲取某個方法上的事務註解屬性
	@Override
	@Nullable
	protected TransactionAttribute findTransactionAttribute(Method method) {
		// Method 實現了 AnnotatedElement 介面,所以交給下面的 
		// determineTransactionAttribute(AnnotatedElement)執行具體的分析邏輯	
		return determineTransactionAttribute(method);
	}

	/**
	 * Determine the transaction attribute for the given method or class.
	 * 分析獲取某個被註解的元素,具體的來講,指的是一個類或者一個方法上的事務註解屬性。
	 * 該實現會遍歷自己屬性annotationParsers中所包含的事務註解屬性分析器試圖獲取事務註解屬性,
	 * 一旦獲取到事務註解屬性則返回,如果獲取不到則返回null,表明目標類/方法上沒有事務註解。
	 */
	@Nullable
	protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
		for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
			TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element);
			if (attr != null) {
				return attr;
			}
		}
		return null;
	}

	/**
	 * By default, only public methods can be made transactional.
	 * 預設情況下,僅僅public方法上的事務註解才被識別和應用
	 */
	@Override
	protected boolean allowPublicMethodsOnly() {
		return this.publicMethodsOnly;
	}


	@Override
	public boolean equals(Object other) {
		if (this == other) {
			return true;
		}
		if (!(other instanceof AnnotationTransactionAttributeSource)) {
			return false;
		}
		AnnotationTransactionAttributeSource otherTas = (AnnotationTransactionAttributeSource) other;
		return (this.annotationParsers.equals(otherTas.annotationParsers) &&
				this.publicMethodsOnly == otherTas.publicMethodsOnly);
	}

	@Override
	public int hashCode() {
		return this.annotationParsers.hashCode();
	}

}

從類 AnnotationTransactionAttributeSource的原始碼可以看到,它僅僅通過protected的方法實現類如何找到指定類/方法的事務註解屬性,但是自身並沒有提供public方法供呼叫者使用。實際上,類 AnnotationTransactionAttributeSource提供給呼叫者的服務方法是public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass),而該方法定義在其父類AbstractFallbackTransactionAttributeSource中。

抽象類 AbstractFallbackTransactionAttributeSource

AbstractFallbackTransactionAttributeSource是介面TransactionAttributeSource的抽象實現,也是上面提到的工具類AnnotationTransactionAttributeSource的父類。方法AbstractFallbackTransactionAttributeSource#getTransactionAttribute是給呼叫者使用的獲取指定方法的事務註解屬性的入口。

方法AbstractFallbackTransactionAttributeSource#getTransactionAttribute接收兩個引數 :

  • method – 目前正在進行的方法呼叫
  • targetClass – 真正要呼叫的方法所在的類

注意這裡細微的差別 : method的宣告類(也就是所屬類)不一定是targetClass,但targetClass一定會有一個方法和method的方法簽名相同。通常情況下,method的宣告類/所屬類會是targetClass的某個祖先類或者實現的某個介面。

方法AbstractFallbackTransactionAttributeSource#getTransactionAttribute按如下順序依次嘗試獲取事務註解屬性 :

  1. specific target method – 和method相同簽名的targetClass上的那個方法
  2. target class – 也就是引數targetClass
  3. declaring method – 也就是引數method
  4. declaring class/interfacemethod的宣告類/所屬類

並對所發現的事務註解屬性進行了快取。

注意 : 如果某個方法和該方法所屬類上都有事務註解屬性,優先使用方法上的事務註解屬性。

package org.springframework.transaction.interceptor;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.aop.support.AopUtils;
import org.springframework.core.MethodClassKey;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;

public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {

	/**
	 * Canonical value held in cache to indicate no transaction attribute was
	 * found for this method, and we don't need to look again.
	 * 針對沒有事務註解屬性的方法進行事務註解屬性快取時使用的特殊值,用於標記該方法沒有事務註解屬性,
	 * 從而不用在首次快取在資訊後,不用再次重複執行真正的分析
	 */
	@SuppressWarnings("serial")
	private static final TransactionAttribute NULL_TRANSACTION_ATTRIBUTE = new DefaultTransactionAttribute() {
		@Override
		public String toString() {
			return "null";
		}
	};


	/**
	 * Logger available to subclasses.
	 * As this base class is not marked Serializable, the logger will be recreated
	 * after serialization - provided that the concrete subclass is Serializable.
	 */
	protected final Log logger = LogFactory.getLog(getClass());

	/**
	 * Cache of TransactionAttributes, keyed by method on a specific target class.
	 * As this base class is not marked Serializable, the cache will be recreated
	 * after serialization - provided that the concrete subclass is Serializable.
	 * TransactionAttribute 
	 * 方法上的事務註解屬性快取,key使用目標類上的方法,使用型別MethodClassKey來表示
	 */
	private final Map<Object, TransactionAttribute> attributeCache = new ConcurrentHashMap<>(1024);


	/**
	 * Determine the transaction attribute for this method invocation.
	 * 獲取指定方法上的註解事務屬性
	 * Defaults to the class's transaction attribute if no method attribute is found.
	 * 如果方法上沒有註解事務屬性,則使用目標方法所屬類上的註解事務屬性
	 * @param method the method for the current invocation (never null)
	 * @param targetClass the target class for this invocation (may be null)
	 * @return a TransactionAttribute for this method, or null if the method
	 * is not transactional
	 */
	@Override
	@Nullable
	public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
		if (method.getDeclaringClass() == Object.class) {
			// 如果目標方法是內建類Object上的方法,總是返回null,這些方法上不應用事務
			return null;
		}

		// First, see if we have a cached value.
		// 先檢視針對該方法是否已經獲取過其註解事務屬性並且已經快取
		Object cacheKey = getCacheKey(method, targetClass);
		TransactionAttribute cached = this.attributeCache.get(cacheKey);
		if (cached != null) {
			// 目標方法上的事務註解屬性資訊已經快取的情況
			// Value will either be canonical value indicating there is no transaction attribute,
			// or an actual transaction attribute.
			if (cached == NULL_TRANSACTION_ATTRIBUTE) {
			// 目標方法上上並沒有事務註解屬性,但是已經被嘗試分析過並且已經被快取,
			// 使用的值是 NULL_TRANSACTION_ATTRIBUTE,所以這裡再次嘗試獲取其註解事務屬性時,
			// 直接返回 null
				return null;
			}
			else {
			// 返回所快取的註解事務屬性
				return cached;
			}
		}
		else {
			// We need to work it out.
			// 目標方法上的註解事務屬性尚未分析過,現在分析獲取之
			TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
			// Put it in the cache.
			if (txAttr == null) {
				// 如果目標方法上並沒有使用註解事務屬性,也快取該資訊,只不過使用的值是一個特殊值:
				// NULL_TRANSACTION_ATTRIBUTE , 此特殊值表明目標方法上沒有使用註解事務屬性
				this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
			}
			else {
				// 目標方法上使用了註解事務屬性,將其放到快取
				String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
				if (txAttr instanceof DefaultTransactionAttribute) {
					((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
				}
				if (logger.isTraceEnabled()) {
					logger.trace("Adding transactional method '" 
						+ methodIdentification + "' with attribute: " + txAttr);
				}
				this.attributeCache.put(cacheKey, txAttr);
			}
			return txAttr;
		}
	}

	/**
	 * Determine a cache key for the given method and target class.
	 * Must not produce same key for overloaded methods.
	 * Must produce same key for different instances of the same method.
	 * @param method the method (never null)
	 * @param targetClass the target class (may be null)
	 * @return the cache key (never null)
	 */
	protected Object getCacheKey(Method method, @Nullable Class<?> targetClass) {
		return new MethodClassKey(method, targetClass);
	}

	/**
	 * 
	 * 查詢目標方法上的事務註解屬性,但只是查詢和返回,並不做快取,效果上講,可以認為
	 * #getTransactionAttribute 是增加了快取機制的方法#computeTransactionAttribute
	 * 
	 * As of 4.1.8, this method can be overridden.
	 * @since 4.1.8
	 * @see #getTransactionAttribute
	 */
	@Nullable
	protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
		// Don't allow no-public methods as required.
		if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
			// 如果事務註解屬性分析僅僅針對public方法,而當前方法不是public,則直接返回null
			return null;
		}

		// The method may be on an interface, but we need attributes from the target class.
		// If the target class is null, the method will be unchanged.
		// 引數 method 可能是基於介面的方法,該介面和引數targetClass所對應的類不同(也就是說:
		// targetClass是相應介面的某個實現類),而我們這裡需要的屬性是要來自targetClass的,
		// 所以這裡先獲取targetClass上的那個和method對應的方法,這裡 method, specificMethod 
		// 都可以認為是潛在的目標方法
		Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

		// First try is the method in the target class.
		// 首先嚐試檢查事務註解屬性直接標記在目標方法 specificMethod 上
		TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
		if (txAttr != null) {
			// 事務註解屬性直接標記在目標方法上
			return txAttr;
		}

		// Second try is the transaction attribute on the target class.
		// 然後嘗試檢查事務註解屬性是否標記在目標方法 specificMethod 所屬類上
		txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
		if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
			// 事務註解屬性是否標記在目標方法所屬類上
			return txAttr;
		}

		// 邏輯走到這裡說明目標方法specificMethod,也就是實現類上的目標方法上沒有標記事務註解屬性
		if (specificMethod != method) {
			// Fallback is to look at the original method.
			// 如果 specificMethod 和 method 不同,則說明 specificMethod 是具體實現類
			// 的方法,method 是實現類所實現介面的方法,現在嘗試從 method 上獲取事務註解屬性
			txAttr = findTransactionAttribute(method);
			if (txAttr != null) {
				return txAttr;
			}
			// Last fallback is the class of the original method.
			// 現在嘗試在 method 所屬類上檢視是否有事務註解屬性
			txAttr = findTransactionAttribute(method.getDeclaringClass());
			if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
				return txAttr;
			}
		}

		// specificMethod 方法/所屬類上沒有事務註解屬性,
		// method 方法/所屬類上也沒有事務註解屬性,
		// 所以返回 null
		return null;
	}


	/**
	 * Subclasses need to implement this to return the transaction attribute for the
	 * given class, if any.
	 * 從指定類上獲取事務註解屬性,由子類負責提供實現
	 * @param clazz the class to retrieve the attribute for
	 * @return all transaction attribute associated with this class, or null if none
	 */
	@Nullable
	protected abstract TransactionAttribute findTransactionAttribute(Class<?> clazz);

	/**
	 * Subclasses need to implement this to return the transaction attribute for the
	 * given method, if any.
	 * 從指定方法上獲取事務註解屬性,由子類負責提供實現
	 * @param method the method to retrieve the attribute for
	 * @return all transaction attribute associated with this method, or null if none
	 */
	@Nullable
	protected abstract TransactionAttribute findTransactionAttribute(Method method);

	/**
	 * Should only public methods be allowed to have transactional semantics?
	 * The default implementation returns false.
	 */
	protected boolean allowPublicMethodsOnly() {
		return false;
	}

}