概述
TransactionAspectSupport
是Spring
的事务切面逻辑抽象基类,该类实现了事务切面逻辑,但是自身设计为不能被直接使用,而是作为抽象基类被实现子类使用,应用于声明式事务使用场景。TransactionInterceptor
,或者 AspectJ
切面类AnnotationTransactionAspect.aj
,JtaAnnotationTransactionAspect.aj
都是继承自该类。
TransactionAspectSupport
为实现子类提供的核心工具方法就是#invokeWithinTransaction
,该方法的实现会把一个对目标方法的调用包裹(可以理解成AOP
中的around
模式)在一个事务处理逻辑中。但是该方法何时被调用,就交给实现子类了。
另外TransactionAspectSupport
使用了策略设计模式(Strategy
)。它会使用一个外部指定的PlatformTransactionManager
来执行事务管理逻辑,并且使用一个外部指定的TransactionAttributeSource
用来获取事务定义信息,也就是@Transactional
这种注解上的信息。
源代码解析
源代码版本:
spring-tx-5.1.5.RELEASE
package org.springframework.transaction.interceptor;
// 省略 import 行
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
// NOTE: This class must not implement Serializable because it serves as base
// class for AspectJ aspects (which are not allowed to implement Serializable)!
/**
* Key to use to store the default transaction manager.
*/
private static final Object DEFAULT_TRANSACTION_MANAGER_KEY = new Object();
/**
* Holder to support the {@code currentTransactionStatus()} method,
* and to support communication between different cooperating advices
* (e.g. before and after advice) if the aspect involves more than a
* single method (as will be the case for around advice).
* 一个ThreadLocal 持有器,用于持有事务执行的相关上下文信息 TransactionInfo,
*/
private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
new NamedThreadLocal<>("Current aspect-driven transaction");
/**
* 供子类用于获取当前 TransactionInfo 的方法。通常仅用于子类需要多个方法
* 协调完成事务管理的情形,比如到AspectJ before/after advice 是不同方法
* 但是要管理同一个事务的情景。而对于 around advice,比如符合AOP联盟
* 规范的这种MethodInterceptor,能在整个切面方法整个生命周期过程中持有
* 对 TransactionInfo 的引用,所以不需要该机制。
* 这里需要注意的是,Spring 事务管理是线程绑定的,而非跨线程的。从上面
* transactionInfoHolder 变量的定义可以看出来。
* Subclasses can use this to return the current TransactionInfo.
* Only subclasses that cannot handle all operations in one method,
* such as an AspectJ aspect involving distinct before and after advice,
* need to use this mechanism to get at the current TransactionInfo.
* An around advice such as an AOP Alliance MethodInterceptor can hold a
* reference to the TransactionInfo throughout the aspect method.
* <p>A TransactionInfo will be returned even if no transaction was created.
* The {@code TransactionInfo.hasTransaction()} method can be used to query this.
* <p>To find out about specific transaction characteristics, consider using
* TransactionSynchronizationManager's {@code isSynchronizationActive()}
* and/or {@code isActualTransactionActive()} methods.
* @return the TransactionInfo bound to this thread, or {@code null} if none
* @see TransactionInfo#hasTransaction()
* @see org.springframework.transaction.support.TransactionSynchronizationManager#isSynchronizationActive()
* @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive()
*/
@Nullable
protected static TransactionInfo currentTransactionInfo() throws NoTransactionException {
return transactionInfoHolder.get();
}
/**
* Return the transaction status of the current method invocation.
* Mainly intended for code that wants to set the current transaction
* rollback-only but not throw an application exception.
* @throws NoTransactionException if the transaction info cannot be found,
* because the method was invoked outside an AOP invocation context
*/
public static TransactionStatus currentTransactionStatus() throws NoTransactionException {
TransactionInfo info = currentTransactionInfo();
if (info == null || info.transactionStatus == null) {
throw new NoTransactionException("No transaction aspect-managed TransactionStatus in scope");
}
return info.transactionStatus;
}
protected final Log logger = LogFactory.getLog(getClass());
// 事务管理器bean的名称
@Nullable
private String transactionManagerBeanName;
// 事务管理器bean对象本身
@Nullable
private PlatformTransactionManager transactionManager;
// 用于获取事务属性的来源对象
@Nullable
private TransactionAttributeSource transactionAttributeSource;
// bean容器自身
@Nullable
private BeanFactory beanFactory;
// 事务管理器缓存,对于非缺省事务管理器,key是事务管理器bean定义上的@Qualifier.value ,
// 对于缺省事务管理器,key是一个对象,由 DEFAULT_TRANSACTION_MANAGER_KEY 定义
private final ConcurrentMap<Object, PlatformTransactionManager> transactionManagerCache =
new ConcurrentReferenceHashMap<>(4);
/**
* Specify the name of the default transaction manager bean.
*/
public void setTransactionManagerBeanName(@Nullable String transactionManagerBeanName) {
this.transactionManagerBeanName = transactionManagerBeanName;
}
/**
* Return the name of the default transaction manager bean.
*/
@Nullable
protected final String getTransactionManagerBeanName() {
return this.transactionManagerBeanName;
}
/**
* Specify the <em>default</em> transaction manager to use to drive transactions.
* <p>The default transaction manager will be used if a <em>qualifier</em>
* has not been declared for a given transaction or if an explicit name for the
* default transaction manager bean has not been specified.
* @see #setTransactionManagerBeanName
*/
public void setTransactionManager(@Nullable PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* Return the default transaction manager, or {@code null} if unknown.
*/
@Nullable
public PlatformTransactionManager getTransactionManager() {
return this.transactionManager;
}
/**
* Set properties with method names as keys and transaction attribute
* descriptors (parsed via TransactionAttributeEditor) as values:
* e.g. key = "myMethod", value = "PROPAGATION_REQUIRED,readOnly".
* <p>Note: Method names are always applied to the target class,
* no matter if defined in an interface or the class itself.
* <p>Internally, a NameMatchTransactionAttributeSource will be
* created from the given properties.
* @see #setTransactionAttributeSource
* @see TransactionAttributeEditor
* @see NameMatchTransactionAttributeSource
*/
public void setTransactionAttributes(Properties transactionAttributes) {
NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
tas.setProperties(transactionAttributes);
this.transactionAttributeSource = tas;
}
/**
* Set multiple transaction attribute sources which are used to find transaction
* attributes. Will build a CompositeTransactionAttributeSource for the given sources.
* @see CompositeTransactionAttributeSource
* @see MethodMapTransactionAttributeSource
* @see NameMatchTransactionAttributeSource
* @see org.springframework.transaction.annotation.AnnotationTransactionAttributeSource
*/
public void setTransactionAttributeSources(TransactionAttributeSource... transactionAttributeSources) {
this.transactionAttributeSource = new CompositeTransactionAttributeSource(transactionAttributeSources);
}
/**
* Set the transaction attribute source which is used to find transaction
* attributes. If specifying a String property value, a PropertyEditor
* will create a MethodMapTransactionAttributeSource from the value.
* 外部设定 TransactionAttributeSource, 用于发现事务属性,比如
* ProxyTransactionManagementConfiguration 中定义了一个实现类型
* 为 AnnotationTransactionAttributeSource 的 TransactionAttributeSource,
* 并最终设置到了 TransactionInterceptor 上,使用的就是此方法
* @see TransactionAttributeSourceEditor
* @see MethodMapTransactionAttributeSource
* @see NameMatchTransactionAttributeSource
* @see org.springframework.transaction.annotation.AnnotationTransactionAttributeSource
*/
public void setTransactionAttributeSource(@Nullable TransactionAttributeSource transactionAttributeSource) {
this.transactionAttributeSource = transactionAttributeSource;
}
/**
* Return the transaction attribute source.
*/
@Nullable
public TransactionAttributeSource getTransactionAttributeSource() {
return this.transactionAttributeSource;
}
/**
* Set the BeanFactory to use for retrieving PlatformTransactionManager beans.
*/
@Override
public void setBeanFactory(@Nullable BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* Return the BeanFactory to use for retrieving PlatformTransactionManager beans.
*/
@Nullable
protected final BeanFactory getBeanFactory() {
return this.beanFactory;
}
/**
* 接口 InitializingBean 定义的初始化方法,这里的实现
* 主要是确保属性 transactionManger, tranfactionAttributeSource 被设置,不为 null
* Check that required properties were set.
*/
@Override
public void afterPropertiesSet() {
if (getTransactionManager() == null && this.beanFactory == null) {
throw new IllegalStateException(
"Set the 'transactionManager' property or make sure to run within a BeanFactory " +
"containing a PlatformTransactionManager bean!");
}
if (getTransactionAttributeSource() == null) {
throw new IllegalStateException(
"Either 'transactionAttributeSource' or 'transactionAttributes' is required: " +
"If there are no transactional methods, then don't use a transaction aspect.");
}
}
/**
* 该方法实现是本类的核心业务 : 将目标方法调用包围在事务处理逻辑中
* General delegate for around-advice-based subclasses, delegating to several other template
* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}
* as well as regular {@link PlatformTransactionManager} implementations.
* @param method the Method being invoked
* @param targetClass the target class that we're invoking the method on
* @param invocation the callback to use for proceeding with the target invocation
* @return the return value of the method, if any
* @throws Throwable propagated from the target invocation
*/
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
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);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 该 if 分支针对 tm 类型不是 CallbackPreferringPlatformTransactionManager 的情况
// 准备事务 : 必要时
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
// 调用目标方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
// 异常时事务机制的处理 :
// 1. 该异常需要回滚事务,则回滚事务
// 2. 该异常无需回滚事务,则提交事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 无论正常还是异常都会发生的事务机制的清场工作 ,
// 比如当前方法的执行需要一个全新的事务,所以该方法执行事前会挂起之前可能存在的事务,
// 现在方法执行完了,需要恢复之前的事务
cleanupTransactionInfo(txInfo);
}
// 目标方法正常执行时提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
final ThrowableHolder throwableHolder = new ThrowableHolder();
// 该 else 分支针对 tm 类型是 CallbackPreferringPlatformTransactionManager 的情况
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
}
}
/**
* Clear the cache.
*/
protected void clearTransactionManagerCache() {
this.transactionManagerCache.clear();
this.beanFactory = null;
}
/**
* Determine the specific transaction manager to use for the given transaction.
* 根据指定的事务属性 txAttr 判断要使用的事务管理器 PlatformTransactionManager
*/
@Nullable
protected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
// Do not attempt to lookup tx manager if no tx attributes are set
if (txAttr == null || this.beanFactory == null) {
// 获取属性 this.transactionManager 作为缺省事务管理器
return getTransactionManager();
}
// 结合考虑事务属性中的 qualifier 确定要使用的事务管理器,可能使用 :
// 1. qualifier 指定的事务管理器
// 2. 使用 this.transactionManagerBeanName 指定的事务管理器
// 3. 使用 容器中唯一存在的类型为 PlatformTransactionManager 的 bean作为
// 缺省事务管理器使用和缓存
String qualifier = txAttr.getQualifier();
if (StringUtils.hasText(qualifier)) {
return determineQualifiedTransactionManager(this.beanFactory, qualifier);
}
else if (StringUtils.hasText(this.transactionManagerBeanName)) {
return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
}
else {
// 获取缺省事务管理器以及相应的缓存机制
PlatformTransactionManager defaultTransactionManager = getTransactionManager();
if (defaultTransactionManager == null) {
defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
if (defaultTransactionManager == null) {
defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
this.transactionManagerCache.putIfAbsent(
DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
}
}
return defaultTransactionManager;
}
}
private PlatformTransactionManager determineQualifiedTransactionManager(BeanFactory beanFactory,
String qualifier) {
PlatformTransactionManager txManager = this.transactionManagerCache.get(qualifier);
if (txManager == null) {
txManager = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
beanFactory, PlatformTransactionManager.class, qualifier);
this.transactionManagerCache.putIfAbsent(qualifier, txManager);
}
return txManager;
}
private String methodIdentification(Method method, @Nullable Class<?> targetClass,
@Nullable TransactionAttribute txAttr) {
String methodIdentification = methodIdentification(method, targetClass);
if (methodIdentification == null) {
if (txAttr instanceof DefaultTransactionAttribute) {
methodIdentification = ((DefaultTransactionAttribute) txAttr).getDescriptor();
}
if (methodIdentification == null) {
methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
}
}
return methodIdentification;
}
/**
* Convenience method to return a String representation of this Method
* for use in logging. Can be overridden in subclasses to provide a
* different identifier for the given method.
* <p>The default implementation returns {@code null}, indicating the
* use of {@link DefaultTransactionAttribute#getDescriptor()} instead,
* ending up as {@link ClassUtils#getQualifiedMethodName(Method, Class)}.
* @param method the method we're interested in
* @param targetClass the class that the method is being invoked on
* @return a String representation identifying this method
* @see org.springframework.util.ClassUtils#getQualifiedMethodName
*/
@Nullable
protected String methodIdentification(Method method, @Nullable Class<?> targetClass) {
return null;
}
/**
* Create a transaction if necessary based on the given TransactionAttribute.
* <p>Allows callers to perform custom TransactionAttribute lookups through
* the TransactionAttributeSource.
* @param txAttr the TransactionAttribute (may be {@code null})
* @param joinpointIdentification the fully qualified method name
* (used for monitoring and logging purposes)
* @return a TransactionInfo object, whether or not a transaction was created.
* The {@code hasTransaction()} method on TransactionInfo can be used to
* tell if there was a transaction created.
* @see #getTransactionAttributeSource()
*/
@SuppressWarnings("serial")
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
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);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
/**
* Prepare a TransactionInfo for the given attribute and status object.
* @param txAttr the TransactionAttribute (may be {@code null})
* @param joinpointIdentification the fully qualified method name
* (used for monitoring and logging purposes)
* @param status the TransactionStatus for the current transaction
* @return the prepared TransactionInfo object
*/
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, String joinpointIdentification,
@Nullable TransactionStatus status) {
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
// We need a transaction for this method...
if (logger.isTraceEnabled()) {
logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
// The transaction manager will flag an error if an incompatible tx already exists.
txInfo.newTransactionStatus(status);
}
else {
// The TransactionInfo.hasTransaction() method will return false. We created it only
// to preserve the integrity of the ThreadLocal stack maintained in this class.
if (logger.isTraceEnabled()) {
logger.trace("Don't need to create transaction for [" + joinpointIdentification +
"]: This method isn't transactional.");
}
}
// We always bind the TransactionInfo to the thread, even if we didn't create
// a new transaction here. This guarantees that the TransactionInfo stack
// will be managed correctly even if no transaction was created by this aspect.
txInfo.bindToThread();
return txInfo;
}
/**
* Execute after successful completion of call, but not after an exception was handled.
* 目标方法返回之后提交事务
* Do nothing if we didn't create a transaction.
* @param txInfo information about the current transaction
*/
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
/**
* Handle a throwable, completing the transaction.
* We may commit or roll back, depending on the configuration.
* 目标方法异常时完成事物的处理,调用者会在此方法之后继续抛出原异常
* @param txInfo information about the current transaction
* @param ex throwable encountered
*/
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
// 当前异常需要触发事务回滚的情况
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
// 当前事务不需要因为异常回滚的情况
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
/**
* Reset the TransactionInfo ThreadLocal.
* <p>Call this in all cases: exception or normal return!
* @param txInfo information about the current transaction (may be {@code null})
*/
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
if (txInfo != null) {
// 恢复方法开始之前的事务信息现场,线程级别
txInfo.restoreThreadLocalStatus();
}
}
/**
* Opaque object used to hold Transaction information. Subclasses
* must pass it back to methods on this class, but not see its internals.
*/
protected final class TransactionInfo {
@Nullable
private final PlatformTransactionManager transactionManager;
@Nullable
private final TransactionAttribute transactionAttribute;
private final String joinpointIdentification;
@Nullable
private TransactionStatus transactionStatus;
@Nullable
private TransactionInfo oldTransactionInfo;
public TransactionInfo(@Nullable PlatformTransactionManager transactionManager,
@Nullable TransactionAttribute transactionAttribute, String joinpointIdentification) {
this.transactionManager = transactionManager;
this.transactionAttribute = transactionAttribute;
this.joinpointIdentification = joinpointIdentification;
}
public PlatformTransactionManager getTransactionManager() {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
return this.transactionManager;
}
@Nullable
public TransactionAttribute getTransactionAttribute() {
return this.transactionAttribute;
}
/**
* Return a String representation of this joinpoint (usually a Method call)
* for use in logging.
*/
public String getJoinpointIdentification() {
return this.joinpointIdentification;
}
public void newTransactionStatus(@Nullable TransactionStatus status) {
this.transactionStatus = status;
}
@Nullable
public TransactionStatus getTransactionStatus() {
return this.transactionStatus;
}
/**
* Return whether a transaction was created by this aspect,
* or whether we just have a placeholder to keep ThreadLocal stack integrity.
*/
public boolean hasTransaction() {
return (this.transactionStatus != null);
}
private void bindToThread() {
// Expose current TransactionStatus, preserving any existing TransactionStatus
// for restoration after this transaction is complete.
this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this);
}
private void restoreThreadLocalStatus() {
// Use stack to restore old transaction TransactionInfo.
// Will be null if none was set.
transactionInfoHolder.set(this.oldTransactionInfo);
}
@Override
public String toString() {
return (this.transactionAttribute != null ? this.transactionAttribute.toString() : "No transaction");
}
}
/**
* Simple callback interface for proceeding with the target invocation.
* Concrete interceptors/aspects adapt this to their invocation mechanism.
*/
@FunctionalInterface
protected interface InvocationCallback {
Object proceedWithInvocation() throws Throwable;
}
/**
* Internal holder class for a Throwable in a callback transaction model.
*/
private static class ThrowableHolder {
@Nullable
public Throwable throwable;
}
/**
* Internal holder class for a Throwable, used as a RuntimeException to be
* thrown from a TransactionCallback (and subsequently unwrapped again).
*/
@SuppressWarnings("serial")
private static class ThrowableHolderException extends RuntimeException {
public ThrowableHolderException(Throwable throwable) {
super(throwable);
}
@Override
public String toString() {
return getCause().toString();
}
}
}