前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【小家Spring】源码分析Spring的事务拦截器:TransactionInterceptor和事务管理器:PlatformTransactionManager

【小家Spring】源码分析Spring的事务拦截器:TransactionInterceptor和事务管理器:PlatformTransactionManager

作者头像
YourBatman
发布2019-09-03 15:36:00
4.3K0
发布2019-09-03 15:36:00
举报
文章被收录于专栏:BAT的乌托邦BAT的乌托邦

前言

接着上一篇博文:

【小家Spring】从基于@Transactional全注解方式的声明式事务入手,彻底掌握Spring事务管理的原理

TransactionInterceptor作为它的增强子,扮演着增强处理Spring事务的核心角色。上篇博文篇幅有限,且为了突出TransactionInterceptorPlatformTransactionManager的重要性,因此本文专门列专文专题讲解~

TransactionInterceptor支撑着整个事务功能的架构,逻辑还是相对复杂的,那么现在我们切入正题来分析此拦截器是如何实现事务特性的。

Spring事务三大接口回顾

在spring的事务管理高层抽象层中主要包含3个接口:

TransactionDefinition:用于描述隔离级别、超时时间、是否为只读事务和事务传播规则
TransactionStatus:代表一个事务的具体运行状态、以及还原点
PlatformTransactionManager:一个高层次的接口,包含3个方法。commitrollbackgetTramsaction

TransactionStatus

在讲解拦截器的执行过程之前,先得了解Spring抽象出来的这个接口。它表示事务的状态

若有需要,事务代码可以使用它来检索状态信息,以编程方式请求回滚(而不是抛出导致隐式回滚的异常)

代码语言:javascript
复制
// 可以看到它继承自SavepointManager,所以它也会处理还原点
public interface TransactionStatus extends SavepointManager, Flushable {

	// 判断当前的事务是否是新事务
	boolean isNewTransaction();
	// 判断该事务里面是否含有还原点~
	boolean hasSavepoint();
	
	// Set the transaction rollback-only
	// 这是了这个,事务的唯一结果是进行回滚。因此如果你在外层给try catche住不让事务回滚,就会抛出你可能常见的异常:
	// Transaction rolled back because it has been marked as rollback-only
	void setRollbackOnly();
	boolean isRollbackOnly();
	
	//将基础会话刷新到数据存储 for example, all affected Hibernate/JPA sessions
	@Override 
	void flush();
	// Return whether this transaction is completed
	// 不管是commit或者rollback了都算结束了~~~
	boolean isCompleted();
}

它的继承体系:

AbstractTransactionStatus

它基本上是对接口进行了一些默认实现:

代码语言:javascript
复制
// @since 1.2.3 
public abstract class AbstractTransactionStatus implements TransactionStatus {
	// 两个标志位
	private boolean rollbackOnly = false;
	private boolean completed = false;

	// 一个还原点
	@Nullable
	private Object savepoint;

	// 把该属性值保存为true
	@Override
	public void setRollbackOnly() {
		this.rollbackOnly = true;
	}
	// 注意此处并不是直接读取
	@Override
	public boolean isRollbackOnly() {
		return (isLocalRollbackOnly() || isGlobalRollbackOnly());
	}
	public boolean isLocalRollbackOnly() {
		return this.rollbackOnly;
	}
	// Global这里返回false 但是子类DefaultTransactionStatus复写了此方法
	public boolean isGlobalRollbackOnly() {
		return false;
	}
	// 啥都没做  也是由子类去实现
	@Override
	public void flush() {
	}
	...
}

下面看看两个具体类的实现:

SimpleTransactionStatus
代码语言:javascript
复制
public class SimpleTransactionStatus extends AbstractTransactionStatus {
	private final boolean newTransaction;

	// 构造函数
	public SimpleTransactionStatus() {
		this(true);
	}
	public SimpleTransactionStatus(boolean newTransaction) {
		this.newTransaction = newTransaction;
	}
	@Override
	public boolean isNewTransaction() {
		return this.newTransaction;
	}
}

它的实现太简单了,就是标志一下事务是否是新的事务。

SimpleTransactionStatus在Spring内部目前还没有使用场景~

DefaultTransactionStatus
代码语言:javascript
复制
public class DefaultTransactionStatus extends AbstractTransactionStatus {
	// 它有很多的标志位,成员变量  
	@Nullable
	private final Object transaction;
	// 是否是新事务
	private final boolean newTransaction;
	// 如果为给定事务打开了新的事务同步  该值为true
	private final boolean newSynchronization;
	// 该事务是否标记为了只读
	private final boolean readOnly;
	private final boolean debug;


	@Nullable
	private final Object suspendedResources;

	// 它的唯一构造函数如下:
	public DefaultTransactionStatus(@Nullable Object transaction, boolean newTransaction, boolean newSynchronization,
			boolean readOnly, boolean debug, @Nullable Object suspendedResources) {
		this.transaction = transaction;
		this.newTransaction = newTransaction;
		this.newSynchronization = newSynchronization;
		this.readOnly = readOnly;
		this.debug = debug;
		this.suspendedResources = suspendedResources;
	}
	
	// 直接把底层事务返回
	public Object getTransaction() {
		Assert.state(this.transaction != null, "No transaction active");
		return this.transaction;
	}
	public boolean hasTransaction() {
		return (this.transaction != null);
	}
	
	// 首先
	@Override
	public boolean isNewTransaction() {
		return (hasTransaction() && this.newTransaction);
	}
	...
	// 都由SmartTransactionObject去处理  该接口的实现类有:
	// JdbcTransactionObjectSupport和JtaTransactionObject(分布式事务)
	public boolean isGlobalRollbackOnly() {
		return ((this.transaction instanceof SmartTransactionObject) &&
				((SmartTransactionObject) this.transaction).isRollbackOnly());
	}
	@Override
	public void flush() {
		if (this.transaction instanceof SmartTransactionObject) {
			((SmartTransactionObject) this.transaction).flush();
		}
	}

}

DefaultTransactionStatus是Spring默认使用的事务状态,后面会有很多的接触~


TransactionStatus有了了解之后,现在正式进去到TransactionInterceptor

TransactionInterceptor:事务拦截器

我们已经知道了,它是个MethodInterceptor,被事务拦截的方法最终都会执行到此增强器身上。

MethodInterceptor是个环绕通知,敲好符合我们的开启、提交、回滚事务等操作~

代码语言:javascript
复制
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
	// 构造函数:
	// 可议不用特殊的指定PlatformTransactionManager 事务管理器,后面会讲解自定义去获取
	// 可议自己指定Properties 以及 TransactionAttributeSource 
	public TransactionInterceptor() {
	}
	public TransactionInterceptor(PlatformTransactionManager ptm, Properties attributes) {
		setTransactionManager(ptm);
		setTransactionAttributes(attributes);
	}
	public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) {
		setTransactionManager(ptm);
		setTransactionAttributeSource(tas);
	}

	// 接下来就是这个invoke方法:就是拦截的入口~
	@Override
	@Nullable
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// 获取目标类
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		// invokeWithinTransaction:父类TransactionAspectSupport的模板方法
		// invocation::proceed本处执行完成  执行目标方法(当然可能还有其余增强器)
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}
	...
}

可以看出,真正做事情的其实还是在父类,它有一个执行事务的模版。

TransactionAspectSupport
代码语言:javascript
复制
// 通过BeanFactoryAware获取到BeanFactory
// InitializingBean的afterPropertiesSet是对Bean做一些验证(经常会借助它这么来校验Bean~~~)
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {

	// 这个类不允许实现Serializable接口,请注意~~
	// 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();
	// currentTransactionStatus() 方法依托于它
	private static final ThreadLocal<TransactionInfo> transactionInfoHolder = new NamedThreadLocal<>("Current aspect-driven transaction");

	//Subclasses can use this to return the current TransactionInfo.
	// Only subclasses that cannot handle all operations in one method
	// 注意此方法是个静态方法  并且是protected的  说明只有子类能够调用,外部并不可以~~~
	@Nullable
	protected static TransactionInfo currentTransactionInfo() throws NoTransactionException {
		return transactionInfoHolder.get();
	}
	
	// 外部调用此Static方法,可议获取到当前事务的状态  从而甚至可议手动来提交、回滚事务
	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;
	}

	//==========================================
	// 事务管理器的名称(若设置,会根据此名称去找到事务管理器~~~~)
	@Nullable
	private String transactionManagerBeanName;
	@Nullable
	private PlatformTransactionManager transactionManager;
	@Nullable
	private TransactionAttributeSource transactionAttributeSource;
	@Nullable
	private BeanFactory beanFactory;

	// 因为事务管理器可能也会有多个  所以此处做了一个简单的缓存~
	private final ConcurrentMap<Object, PlatformTransactionManager> transactionManagerCache = new ConcurrentReferenceHashMap<>(4);
	
	public void setTransactionAttributeSource(@Nullable TransactionAttributeSource transactionAttributeSource) {
		this.transactionAttributeSource = transactionAttributeSource;
	}
	// 这部操作发现,若传入的为Properties  内部是实际使用的是NameMatchTransactionAttributeSource 去匹配的
	// 备注:若调用了此方法   transactionAttributeSource就会被覆盖的哟
	public void setTransactionAttributes(Properties transactionAttributes) {
		NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
		tas.setProperties(transactionAttributes);
		this.transactionAttributeSource = tas;
	}
	// 若你有多种匹配策略,这也是支持的  可谓非常强大有木有~~~
	public void setTransactionAttributeSources(TransactionAttributeSource... transactionAttributeSources) {
		this.transactionAttributeSource = new CompositeTransactionAttributeSource(transactionAttributeSources);
	}
	...
	// 接下来就只剩我们最为核心的处理事务的模版方法了:
	//protected修饰,不允许其他包和无关类调用
	@Nullable
	protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable {

		// 获取事务属性源~
		TransactionAttributeSource tas = getTransactionAttributeSource();
		// 获取该方法对应的事务属性(这个特别重要)
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);

		// 这个厉害了:就是去找到一个合适的事务管理器(具体策略详见方法~~~)
		final PlatformTransactionManager tm = determineTransactionManager(txAttr);
		// 拿到目标方法唯一标识(类.方法,如service.UserServiceImpl.save)
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		// 如果txAttr为空或者tm 属于非CallbackPreferringPlatformTransactionManager,执行目标增强
		// 在TransactionManager上,CallbackPreferringPlatformTransactionManager实现PlatformTransactionManager接口,暴露出一个方法用于执行事务处理中的回调
		if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
		
			// 看是否有必要创建一个事务,根据`事务传播行为`,做出相应的判断
			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
			
			Object retVal = null;
			try {
				//回调方法执行,执行目标方法(原有的业务逻辑)
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// 出现异常了,进行回滚(注意:并不是所有异常都会rollback的)
				// 备注:此处若没有事务属性   会commit 兼容编程式事务吧
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				//清除信息
				cleanupTransactionInfo(txInfo);
			}
	
			// 目标方法完全执行完成后,提交事务~~~
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

		//编程式事务处理(CallbackPreferringPlatformTransactionManager) 会走这里 
		// 原理也差不太多,这里不做详解~~~~
		else {
			final ThrowableHolder throwableHolder = new ThrowableHolder();

			// 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;
			}
		}
	}

	// 从容器中找到一个事务管理器
	@Nullable
	protected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
		// 如果这两个都没配置,所以肯定是手动设置了PlatformTransactionManager的,那就直接返回即可
		if (txAttr == null || this.beanFactory == null) {
			return getTransactionManager();
		}

		// qualifier 就在此处发挥作用了,他就相当于BeanName
		String qualifier = txAttr.getQualifier();
		if (StringUtils.hasText(qualifier)) {
			// 根据此名称 以及PlatformTransactionManager.class 去容器内招
			return determineQualifiedTransactionManager(this.beanFactory, qualifier);
		}
		// 若没有指定qualifier   那再看看是否指定了 transactionManagerBeanName
		else if (StringUtils.hasText(this.transactionManagerBeanName)) {
			return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
		}
		// 若都没指定,那就不管了。直接根据类型去容器里找 getBean(Class)
		// 此处:若容器内有两个PlatformTransactionManager ,那就铁定会报错啦~~~
		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;
		}
	}
	// ======================================
}

不同的事务处理方式使用不同的逻辑。对于声明式事务的处理与编程式事务的处理,重要区别在于事务属性上,因为编程式的事务处理是不需要有事务属性的

上面处理事务的模版已经分析完成,下面单独摘出来分析一些具体的非常重要的方法。

TransactionAspectSupport.TransactionInfo:内部类 事务Info

它是TransactionAspectSupport的一个protected内部类。我觉得有必要先讲解下它:

代码语言:javascript
复制
	protected final class TransactionInfo {
		// 当前事务  的事务管理器
		@Nullable
		private final PlatformTransactionManager transactionManager;
		// 当前事务  的事务属性
		@Nullable
		private final TransactionAttribute transactionAttribute;
		// joinpoint标识
		private final String joinpointIdentification;
		// 当前事务 	的TransactionStatus 
		@Nullable
		private TransactionStatus transactionStatus;

		// 重点就是这个oldTransactionInfo字段
		// 这个字段保存了当前事务所在的`父事务`上下文的引用,构成了一个链,准确的说是一个有向无环图
		@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;
		}
		
		// 注意这个方法名,新的一个事务status
		public void newTransactionStatus(@Nullable TransactionStatus status) {
			this.transactionStatus = status;
		}
		public boolean hasTransaction() {
			return (this.transactionStatus != null);
		}

		//绑定当前正在处理的事务的所有信息到ThreadLocal
		private void bindToThread() {
			// Expose current TransactionStatus
			// 老的事务  先从线程中拿出来,再把新的(也就是当前)绑定进去~~~~~~
			this.oldTransactionInfo = transactionInfoHolder.get();
			transactionInfoHolder.set(this);
		}

		// 当前事务处理完之后,恢复父事务上下文
		private void restoreThreadLocalStatus() {
			transactionInfoHolder.set(this.oldTransactionInfo);
		}

		@Override
		public String toString() {
			return (this.transactionAttribute != null ? this.transactionAttribute.toString() : "No transaction");
		}
	}

了解了这个Info内部类之后,接下来可议看看TransactionAspectSupport#createTransactionIfNecessary方法:它是创建事务的一个重要方法。

它会判断是否存在事务,根据事务的传播属性。做出不同的处理,也是做了一层包装,核心是通过TransactionStatus来判断事务的属性

创建事务:createTransactionIfNecessary()
代码语言:javascript
复制
	// 若有需要 创建一个TransactionInfo (具体的事务从事务管理器里面getTransaction()出来~)
	protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

		// If no name specified, apply method identification as transaction name.
		// 这个简单的说,就是给Name赋值~~~~~
		if (txAttr != null && txAttr.getName() == null) {
			txAttr = new DelegatingTransactionAttribute(txAttr) {
				@Override
				public String getName() {
					return joinpointIdentification;
				}
			};
		}

		// 从事务管理器里,通过txAttr拿出来一个TransactionStatus
		TransactionStatus status = null;
		if (txAttr != null) {
			if (tm != null) {
				status = tm.getTransaction(txAttr);
			}
			...
		}
		// 通过TransactionStatus 等,转换成一个通用的TransactionInfo
		return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}
准备事务:prepareTransactionInfo()
代码语言:javascript
复制
	protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, String joinpointIdentification,
			@Nullable TransactionStatus status) {

		// 构造一个TransactionInfo 
		TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
		if (txAttr != null) {
			// 如果已存在不兼容的Tx,事务管理器将标记错误
			txInfo.newTransactionStatus(status);
		}
		,..

		// 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.
		// 这句话是最重要的:把生成的TransactionInfo并绑定到当前线程的ThreadLocal
		txInfo.bindToThread();
		return txInfo;
	}
提交事务:commitTransactionAfterReturning()
代码语言:javascript
复制
	//比较简单  只用用事务管理器提交事务即可~~~  具体的实现逻辑在事务管理器的commit实现里~~~
	protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
		}
	}
回滚事务:completeTransactionAfterThrowing()
代码语言:javascript
复制
	protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {

		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			// 如果有事务属性了,那就调用rollbackOn看看这个异常需不需要回滚
			if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
				try {
					txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
				}
				...
			}
			// 编程式事务没有事务属性,那就commit吧
			else {
				try {
					txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
				}
				...
			}
		}
	}
清除(解绑)事务:cleanupTransactionInfo()
代码语言:javascript
复制
	protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
		if (txInfo != null) {
			txInfo.restoreThreadLocalStatus();
		}
	}

PlatformTransactionManager:事务管理器

记下硕说另外一大块内容:事务管理器。谈到事务,肯定是离不开它的。

关于事务管理器,不管是JPA(**JpaTransactionManager** )还是JDBC(**DataSourceTransactionManager**)甚至是JTA(**JtaTransactionManager**)等都实现自接口 PlatformTransactionManager

它只提供三个方法:

代码语言:javascript
复制
public interface PlatformTransactionManager {
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
	void commit(TransactionStatus status) throws TransactionException;
	void rollback(TransactionStatus status) throws TransactionException;
}

这三个方法就不用再多说。

下面以最常用的实现:DataSourceTransactionManager 做源码分析~~~

先看看它的抽象实现:AbstractPlatformTransactionManager,这个抽象类提供了处理的模版(其实Spring的设计模式中,很多抽象类都提供了实现模版),然后提供开口给子类去各自实现~

AbstractPlatformTransactionManager

可见它是对PlatformTransactionManager的一个抽象实现。实现Spring的标准事务工作流

这个基类提供了以下工作流程处理:

  • 确定如果有现有的事务;
  • 应用适当的传播行为;
  • 如果有必要暂停和恢复事务;
  • 提交时检查rollback-only标记;
  • 应用适当的修改当回滚(实际回滚或设置rollback-only);
  • 触发同步回调注册(如果事务同步是激活的)
代码语言:javascript
复制
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {

	//始终激活事务同步(请参阅事务的传播属性~)
	public static final int SYNCHRONIZATION_ALWAYS = 0;
	//仅对实际事务(即,不针对由传播导致的空事务)激活事务同步\不支持现有后端事务
	public static final int SYNCHRONIZATION_ON_ACTUAL_TRANSACTION = 1;
	//永远不激活事务同步
	public static final int SYNCHRONIZATION_NEVER = 2;

	// 相当于把本类的所有的public static final的变量都收集到此处~~~~
	private static final Constants constants = new Constants(AbstractPlatformTransactionManager.class);

	// ===========默认值
	private int transactionSynchronization = SYNCHRONIZATION_ALWAYS;
	// 事务默认的超时时间  为-1表示不超时
	private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;
	//Set whether nested transactions are allowed. Default is "false".
	private boolean nestedTransactionAllowed = false;
	// Set whether existing transactions should be validated before participating(参与、加入)
	private boolean validateExistingTransaction = false;
	
	//设置是否仅在参与事务`失败后`将 现有事务`全局`标记为回滚  默认值是true 需要注意~~~
	// 表示只要你的事务失败了,就标记此事务为rollback-only 表示它只能给与回滚  而不能再commit或者正常结束了
	// 这个调用者经常会犯的一个错误就是:上层事务service抛出异常了,自己把它给try住,并且并且还不throw,那就肯定会报错的:
	// 报错信息:Transaction rolled back because it has been marked as rollback-only
	// 当然喽,这个属性强制不建议设置为false~~~~~~
	private boolean globalRollbackOnParticipationFailure = true;
	// 如果事务被全局标记为仅回滚,则设置是否及早失败~~~~
	private boolean failEarlyOnGlobalRollbackOnly = false;
	// 设置在@code docommit调用失败时是否应执行@code dorollback 通常不需要,因此应避免
	private boolean rollbackOnCommitFailure = false;
	
	// 此处我们直接可以通过属性们来社会,语意思更清晰些了
	// 我们发现使用起来有点枚举的意思了,特别是用XML配置的时候  非常像枚举的使用~~~~~~~
	// 这也是Constants的重要意义~~~~
	public final void setTransactionSynchronizationName(String constantName) {
		setTransactionSynchronization(constants.asNumber(constantName).intValue());
	}
	public final void setTransactionSynchronization(int transactionSynchronization) {
		this.transactionSynchronization = transactionSynchronization;
	}
	//... 省略上面所有字段的一些get/set方法~~~

	// 最为重要的一个方法,根据实物定义,获取到一个事务TransactionStatus 
	@Override
	public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
		//doGetTransaction()方法是抽象方法,具体的实现由具体的事务处理器提供(下面会以DataSourceTransactionManager为例子)
		Object transaction = doGetTransaction();

		//如果没有配置事务属性,则使用默认的事务属性
		if (definition == null) {
			definition = new DefaultTransactionDefinition();
		}

		//检查当前线程是否存在事务  isExistingTransaction此方法默认返回false  但子类都复写了此方法
		if (isExistingTransaction(transaction)) {
			// handleExistingTransaction方法为处理已经存在事务的情况
			// 这个方法的实现也很复杂,总之还是对一些传播属性进行解析,各种情况的考虑~~~~~ 如果有新事务产生 doBegin()就会被调用~~~~
			return handleExistingTransaction(definition, transaction, debugEnabled);
		}

		// 超时时间的简单校验~~~~
		if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
			throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
		}

		// 处理事务属性中配置的事务传播特性==============
	
		// PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");
		}
	
		//如果事务传播特性为required、required_new或nested
		else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
				
			// 挂起,但是doSuspend()由子类去实现~~~
			// 挂起操作,触发相关的挂起注册的事件,把当前线程事物的所有属性都封装好,放到一个SuspendedResourcesHolder
			// 然后清空清空一下`当前线程事务`
			SuspendedResourcesHolder suspendedResources = suspend(null);

			// 此处,开始创建事务~~~~~
			try {
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);

				// //创建一个新的事务状态  就是new DefaultTransactionStatus()  把个属性都赋值上
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
				// 开始事务,抽象方法,由子类去实现~
				doBegin(transaction, definition);
				//初始化和同步事务状态    是TransactionSynchronizationManager这个类  它内部维护了很多的ThreadLocal
				prepareSynchronization(status, definition);
				return status;
			}
			catch (RuntimeException | Error ex) {
				//重新开始 doResume由子类去实现
				resume(null, suspendedResources);
				throw ex;
			}
		}
		// 走到这里  传播属性就是不需要事务的  那就直接创建一个
		else {
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			// 这个方法相当于先newTransactionStatus,再prepareSynchronization这两步~~~
			// 显然和上面的区别是:中间不回插入调用doBegin()方法,因为没有事务  begin个啥~~
			return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
		}
	}


	// 再看看commit方法
	@Override
	public final void commit(TransactionStatus status) throws TransactionException {
		//如果是一个已经完成的事物,不可重复提交
		if (status.isCompleted()) {
			throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");
		}

		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
		// 如果已经标记为了需要回滚,那就执行回滚吧
		if (defStatus.isLocalRollbackOnly()) {
			processRollback(defStatus, false);
			return;
		}

		//  shouldCommitOnGlobalRollbackOnly这个默认值是false,目前只有JTA事务复写成true了
		// isGlobalRollbackOnly:是否标记为了全局的RollbackOnly
		if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
			processRollback(defStatus, true);
			return;
		}
		// 提交事务   这里面还是挺复杂的,会考虑到还原点、新事务、事务是否是rollback-only之类的~~
		processCommit(defStatus);
	}

	// rollback方法  里面doRollback方法交给子类去实现~~~
	@Override
	public final void rollback(TransactionStatus status) throws TransactionException {
		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
		processRollback(defStatus, false);
	}
}

从这个抽象类源码分析可以看出,它绝对是一个非常非常典型的模版实现,各个方法实现都是这样。自己先提供实现模版,很多具体的实现方案都开放给子类,比如begin,suspend, resume, commit, rollback等,相当于留好了众多的连接点

这个类的抽象程度非常的高,逻辑也非常的复杂。要想绝对的理解到位,必须要对JDBC的事务非常了解,而且还对这些代码逻辑必须进行精读。书读百遍,其义自见

DataSourceTransactionManager

本文就以最为常用DataSourceTransactionManager作为实现类,简单的看看实现类具体做了什么~

代码语言:javascript
复制
// 它还实现了ResourceTransactionManager接口,提供了getResourceFactory()方法
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, InitializingBean {
	// 显然它管理的就是DataSource  而JTA分布式事务管理可能就是各种各样的数据源了
	@Nullable
	private DataSource dataSource;
	// 不要强制标记为ReadOnly
	private boolean enforceReadOnly = false;

	// JDBC默认是允许内嵌的事务的
	public DataSourceTransactionManager() {
		setNestedTransactionAllowed(true);
	}
	public DataSourceTransactionManager(DataSource dataSource) {
		this();
		setDataSource(dataSource);
		// 它自己的InitializingBean也是做了一个简单的校验而已~~~
		afterPropertiesSet();
	}

	// 手动设置数据源
	public void setDataSource(@Nullable DataSource dataSource) {
		// 这步处理有必要
		// TransactionAwareDataSourceProxy是对dataSource 的包装
		if (dataSource instanceof TransactionAwareDataSourceProxy) {
			this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
		} else {
			this.dataSource = dataSource;
		}
	}

	//Return the JDBC DataSource
	@Nullable
	public DataSource getDataSource() {
		return this.dataSource;
	}
	// @since 5.0 Spring5.0提供的方法   其实还是调用的getDataSource()  判空了而已
	protected DataSource obtainDataSource() {
		DataSource dataSource = getDataSource();
		Assert.state(dataSource != null, "No DataSource set");
		return dataSource;
	}
	// 直接返回的数据源~~~~
	@Override
	public Object getResourceFactory() {
		return obtainDataSource();
	}
	...
	// 这里返回的是一个`DataSourceTransactionObject`
	// 它是一个`JdbcTransactionObjectSupport`,所以它是SavepointManager、实现了SmartTransactionObject接口
	@Override
	protected Object doGetTransaction() {
		DataSourceTransactionObject txObject = new DataSourceTransactionObject();
		txObject.setSavepointAllowed(isNestedTransactionAllowed());
		// 这个获取有意思~~~~相当于按照线程来的~~~
		ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
		txObject.setConnectionHolder(conHolder, false);
		return txObject;
	}

	// 检查当前事务是否active
	@Override
	protected boolean isExistingTransaction(Object transaction) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
	}


	// 这是一个核心内容了,里面逻辑需要分析分析~~~
	@Override
	protected void doBegin(Object transaction, TransactionDefinition definition) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		Connection con = null;

		try {
			if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
				// 从DataSource里获取一个连接(这个DataSource一般是有连接池的~~~)
				Connection newCon = obtainDataSource().getConnection();
				// 把这个链接用ConnectionHolder包装一下~~~
				txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
			}

			txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
			con = txObject.getConnectionHolder().getConnection();
			
			// 设置isReadOnly、设置隔离界别等~
			Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
			txObject.setPreviousIsolationLevel(previousIsolationLevel);

			// 这里非常的关键,先看看Connection 是否是自动提交的
			// 如果是 就con.setAutoCommit(false)  要不然数据库默认没执行一条SQL都是一个事务,就没法进行事务的管理了
			if (con.getAutoCommit()) {
				txObject.setMustRestoreAutoCommit(true);
				con.setAutoCommit(false);
			}
			// ====因此从这后面,通过此Connection执行的所有SQL语句只要没有commit就都不会提交给数据库的=====
			
			// 这个方法特别特别有意思   它自己`Statement stmt = con.createStatement()`拿到一个Statement
			// 然后执行了一句SQL:`stmt.executeUpdate("SET TRANSACTION READ ONLY");`
			// 所以,所以:如果你仅仅只是查询。把事务的属性设置为readonly=true  Spring对帮你对SQl进行优化的
			// 需要注意的是:readonly=true 后,只能读,不能进行dml操作)(只能看到设置事物前数据的变化,看不到设置事物后数据的改变)
			prepareTransactionalConnection(con, definition);
			txObject.getConnectionHolder().setTransactionActive(true);

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

			// Bind the connection holder to the thread.
			// 这一步:就是把当前的链接 和当前的线程进行绑定~~~~
			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);
		}
	}

	// 真正提交事务
	@Override
	protected void doCommit(DefaultTransactionStatus status) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		// 拿到链接  然后直接就commit了   
		Connection con = txObject.getConnectionHolder().getConnection();
		try {
			con.commit();
		} catch (SQLException ex) {
			throw new TransactionSystemException("Could not commit JDBC transaction", ex);
		}
	}
	//doRollback()方法也类似  这里不再细说
}

事务属性readonly=true 后,只能读,不能进行dml操作)(只能看到设置事物前数据的变化,看不到设置事物后数据的改变) 但是但是但是通过源码我发现,你光@Transactional(readOnly = true)这样是不够的,还必须在配置DataSourceTransactionManager的时候,来这么一句dataSourceTransactionManager.setEnforceReadOnly(true),最终才会对你的只读事务进行优化

难道这是Spring的坑???

其实不是,其实如果仅仅只是来了这么一句@Transactional(readOnly = true)而已,最终会把这个Connection设置为只读:con.setReadOnly(true); 它表示将此**连接**设置为只读模式,作为驱动程序启用数据库优化的提示。 将链接设置为只读模式通知数据库后,数据库会对做自己的只读优化。

但是但是但是,这对数据库而言不一定对于数据库而言这就是readonly事务,这点是非常重要的。(因为毕竟一个事务内可能有多个链接)

因此若想它变成只读性事务,进行最大程度上的优化,那么请你配置上的时候加上这一句:

代码语言:javascript
复制
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);
        dataSourceTransactionManager.setEnforceReadOnly(true); // 让事务管理器进行只读事务层面上的优化  建议开启
        return dataSourceTransactionManager;
    }

基于这个特性,我在我的工作中强烈建议Controller层、Service层甚至Dao层各单元都进行读写分离,这样对读这一层能进行很好的统一优化,提升统一管控的效率

附:JDBC几个重要的API

  • 关闭自动提交:java.sql.Connection.setAutoCommit(false) 若是true,每次操作都被认为是一次提交
  • 手动提交事务:con.commit();
  • 出现异常时回滚,不一定在catch语句中,只要在con.commit()前需要回滚时执行都可:con.rollback();
  • 关闭连接:con.close();
  • 设置事务隔离级别: java.sql.Connection#setTransactionIsolation()
总结

可能有小伙伴初看这篇博文,一脸懵逼,我觉得这是正常的行为表现。

现在的JavaEE开发得益于Spring框架优秀的封装,很多底层的东西我们不用再去关系,面向应用进行开发即可。

所以说工具也是一把双刃剑,它的好处是非常多的,能让你实现快速开发、维护。当然弊端就是屏蔽了太多的底层的实现,使得我们这代程序员大都越来越不会码代码了~~

说归说,实现快速开发,高可维护性又必然是永远的趋势。所以会用、熟练的使用永远摆在第一位~

个人建议看任何问题都需要有辩证性的思维。肯定不是不推荐使用工具(毕竟我认为重复造轮子是在浪费生命),相反反而是更加的推崇。但是我们需要知道它背后得设计思路、设计原理,才能做到心中有数,更加运用自如。更重要的是扩展起来也是才能胸有成足,做到有把握:

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年04月29日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring事务三大接口回顾
  • TransactionStatus
    • AbstractTransactionStatus
    • TransactionInterceptor:事务拦截器
      • TransactionAspectSupport
      • PlatformTransactionManager:事务管理器
        • AbstractPlatformTransactionManager
          • DataSourceTransactionManager
            • 总结
            相关产品与服务
            数据库
            云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档