问题

面试中是不是有时经常会被问到 “Spring 事务如何管理的了解吗?” ,“Spring 事务的传播性有哪些,能聊聊它们的使用场景吗?”, “事务回滚的时候是所有异常下都会回滚吗?”; 下面我们就带着这些问题来看看 Spring 事务是如何实现的吧。

实现分析

首先我们还是先通过一个使用示例,先看下 Spring 事务是如何工作的。

使用示例

本文我们先采用 TransactionProxyFactoryBean 配置的方式来看下, Spring 事务如何实现

<beans><!-- 配置数据源 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" ><property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property><property name="url"><value>url</value></property><property name="username"><value>username</value></property><property name="password"><value>password</value></property></bean><!-- 配置事务管理 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource"><ref bean="dataSource"></ref></property></bean><!-- 配置 jdbcTemplate --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource"><ref bean="dataSource"/></property></bean><bean id="userServiceTarget" class="org.springframework.transaction.UserServiceImpl"><property name="jdbcTemplate"><ref bean="jdbcTemplate"></ref></property></bean><!--TransactionProxyFactoryBean 实现了接口 InitializingBean,在初始化过程中会调用 afterPropertiesSet1 : 创建事务拦截器2 : 创建事务 advisor (事务的拦截切面)3 : 创建代理--><bean id="userService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name="transactionManager"><ref bean="transactionManager" /></property><property name="target"><ref bean="userServiceTarget"></ref></property><property name="proxyInterfaces"><value>org.springframework.transaction.UserService</value></property><!--配置事务属性 传播行为, 事务隔离级别, 是否只读, 回滚规则(哪些异常下执行回滚),key 为配置需要事务管理的方法名;在代理目标执行的时候会通过该属性判断方法是否需要事务管理--><property name="transactionAttributes"><props><prop key="*">PROPAGATION_REQUIRED</prop></props></property></bean>
</beans>
复制代码

TransactionProxyFactoryBean 的属性配置中如果您对 transactionAttributes 属性不熟悉的话,是不是会感觉一头雾水呢? 这个玩意怎么配置的? 配置格式又是什么样的呢? 配置值有哪些呢 ?; 下面将会通过对 TransactionProxyFactoryBean 的源码分析来一一解答。

源码分析

类结构

TransactionProxyFactoryBean 类结构我们知道,其实现了接口 InitializingBeanFactoryBean; 那么也就是在 TransactionProxyFactoryBean 实例化后会调用方法 afterPropertiesSet, 在获取目标对象实例时会调用方法 getObject; 下面将主要看下这两个方法的实现。

afterPropertiesSet-创建目标代理对象

public void afterPropertiesSet() throws AopConfigException {// 校验 Target 目标对象if (this.target == null) {throw new AopConfigException("Target must be set");}// 校验事务属性定义,从抛出的异常信息可以看出 Spring 在此做了强校验;// 也就是说如果没有需要 Spring 事务管理的方法,就不要采用 TransactionProxyFactoryBean 了// 那么 transactionAttributeSource 是怎么来的呢? 见下文分析if (this.transactionAttributeSource == null) {throw new AopConfigException("Either 'transactionAttributeSource' or 'transactionAttributes' is required: " +"If there are no transactional methods, don't use a transactional proxy.");}// 创建事务拦截器 transactionInterceptorTransactionInterceptor transactionInterceptor = new TransactionInterceptor();transactionInterceptor.setTransactionManager(this.transactionManager);transactionInterceptor.setTransactionAttributeSource(this.transactionAttributeSource);transactionInterceptor.afterPropertiesSet();ProxyFactory proxyFactory = new ProxyFactory();// 是否配置了前置拦截if (this.preInterceptors != null) {for (int i = 0; i < this.preInterceptors.length; i++) {proxyFactory.addAdvisor(GlobalAdvisorAdapterRegistry.getInstance().wrap(this.preInterceptors[i]));}}if (this.pointcut != null) {// 如果配置了 pointcut 切入点,则按配置的 pointcut 创建 advisorAdvisor advice = new DefaultPointcutAdvisor(this.pointcut, transactionInterceptor);proxyFactory.addAdvisor(advice);}else {// rely on default pointcut// 创建事务拦截切面 advisorproxyFactory.addAdvisor(new TransactionAttributeSourceAdvisor(transactionInterceptor));// could just do the following, but it's usually less efficient because of AOP advice chain caching// proxyFactory.addInterceptor(transactionInterceptor);}// 是否配置了后置拦截if (this.postInterceptors != null) {for (int i = 0; i < this.postInterceptors.length; i++) {proxyFactory.addAdvisor(GlobalAdvisorAdapterRegistry.getInstance().wrap(this.postInterceptors[i]));}}proxyFactory.copyFrom(this);proxyFactory.setTargetSource(createTargetSource(this.target));// 设置代理的接口if (this.proxyInterfaces != null) {proxyFactory.setInterfaces(this.proxyInterfaces);}else if (!getProxyTargetClass()) {// rely on AOP infrastructure to tell us what interfaces to proxyproxyFactory.setInterfaces(AopUtils.getAllInterfaces(this.target));}// 创建目标对象的代理对象this.proxy = proxyFactory.getProxy();
}
复制代码

从源码中我们知道 afterPropertiesSet 主要做以下几件事:

  • 参数有效性校验; 校验目标对象,事务属性定义
  • 设置代理的 advisor chain, 包括用户自定义的前置拦截, 内置的事务拦截器,用户自定义的后置拦截
  • 创建目标代理对象

afterPropertiesSet 的实现中有个针对 transactionAttributeSource 的非空校验,那么这个变量是何时赋值的呢 ? 还记得使用示例中的关于事务属性的定义 transactionAttributes 吗 ?

setTransactionAttributes-设置事务属性定义

public void setTransactionAttributes(Properties transactionAttributes) {NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();tas.setProperties(transactionAttributes);this.transactionAttributeSource = tas;
}public void setProperties(Properties transactionAttributes) {TransactionAttributeEditor tae = new TransactionAttributeEditor();// 遍历 propertiesfor (Iterator it = transactionAttributes.keySet().iterator(); it.hasNext(); ) {// key 为匹配的方法名String methodName = (String) it.next();String value = transactionAttributes.getProperty(methodName);// 解析 valuetae.setAsText(value);TransactionAttribute attr = (TransactionAttribute) tae.getValue();// 将方法名与事务属性定义匹配关联addTransactionalMethod(methodName, attr);}
}
复制代码

下面我们就看下 setAsText 方法是如何解析事务属性的配置

/*** Format is PROPAGATION_NAME,ISOLATION_NAME,readOnly,+Exception1,-Exception2.* Null or the empty string means that the method is non transactional.* @see java.beans.PropertyEditor#setAsText(java.lang.String)*/
public void setAsText(String s) throws IllegalArgumentException {if (s == null || "".equals(s)) {setValue(null);}else {// tokenize it with ","// 按 , 分割配置信息String[] tokens = StringUtils.commaDelimitedListToStringArray(s);RuleBasedTransactionAttribute attr = new RuleBasedTransactionAttribute();for (int i = 0; i < tokens.length; i++) {String token = tokens[i];// 以 PROPAGATION 开头,则配置事务传播性if (token.startsWith(TransactionDefinition.PROPAGATION_CONSTANT_PREFIX)) {attr.setPropagationBehaviorName(tokens[i]);}// 以 ISOLATION 开头,则配置事务隔离级别else if (token.startsWith(TransactionDefinition.ISOLATION_CONSTANT_PREFIX)) {attr.setIsolationLevelName(tokens[i]);}// 以 timeout_ 开头,则设置事务超时时间else if (token.startsWith(DefaultTransactionAttribute.TIMEOUT_PREFIX)) {String value = token.substring(DefaultTransactionAttribute.TIMEOUT_PREFIX.length());attr.setTimeout(Integer.parseInt(value));}// 若等于 readOnly 则配置事务只读else if (token.equals(DefaultTransactionAttribute.READ_ONLY_MARKER)) {attr.setReadOnly(true);}// 以 + 开头,则配置哪些异常下不回滚else if (token.startsWith(DefaultTransactionAttribute.COMMIT_RULE_PREFIX)) {attr.getRollbackRules().add(new NoRollbackRuleAttribute(token.substring(1)));}// 以 - 开头,则配置哪些异常下回滚else if (token.startsWith(DefaultTransactionAttribute.ROLLBACK_RULE_PREFIX)) {attr.getRollbackRules().add(new RollbackRuleAttribute(token.substring(1)));}else {throw new IllegalArgumentException("Illegal transaction token: " + token);}}setValue(attr);}
}
复制代码

setAsText 方法的实现我们就可以搞明白在配置文件中 transactionAttributes 如何配置了,譬如:

<property name="transactionAttributes"><props><prop key="*">PROPAGATION_REQUIRED, ISOLATION_DEFAULT, readOnly</prop></props>
</property>
复制代码

也可以这样配置:

<property name="transactionAttributes"><props><prop key="*">readOnly, ISOLATION_DEFAULT, PROPAGATION_REQUIRED</prop></props>
</property>
复制代码

也就是说 transactionAttributes 的配置只要保证 token 格式正确即可,顺序无关;但是从规范来讲建议还是保持 PROPAGATION_NAME,ISOLATION_NAME,readOnly,+Exception1,-Exception2. 的格式。

getObject-获取代理对象

public Object getObject() {// proxy 对象在 afterPropertiesSet 方法执行时产生return this.proxy;
}
复制代码

代理执行

是否支持事务

重拾-Spring AOP 中我们知道,当代理对象在执行的时候会先获取当前方法所匹配的 advisor (参见类 JdkDynamicAopProxy); 而 TransactionProxyFactoryBean 在创建代理对象的时候会将 TransactionInterceptor 绑定到 TransactionAttributeSourceAdvisor 上,那么我就看下 TransactionAttributeSourceAdvisor 是如何匹配方法的。

public class TransactionAttributeSourceAdvisor extends StaticMethodMatcherPointcutAdvisor {private TransactionAttributeSource transactionAttributeSource;public TransactionAttributeSourceAdvisor(TransactionInterceptor ti) {super(ti);if (ti.getTransactionAttributeSource() == null) {throw new AopConfigException("Cannot construct a TransactionAttributeSourceAdvisor using a " +"TransactionInterceptor that has no TransactionAttributeSource configured");}this.transactionAttributeSource = ti.getTransactionAttributeSource();}public boolean matches(Method m, Class targetClass) {return (this.transactionAttributeSource.getTransactionAttribute(m, targetClass) != null);}
}
复制代码

TransactionAttributeSourceAdvisor 判断方法是否匹配时,实际是由 NameMatchTransactionAttributeSource 的方法 getTransactionAttribute 来处理。

public TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {// 获取目标方法名String methodName = method.getName();// 获取目标方法匹配的事务属性定义TransactionAttribute attr = (TransactionAttribute) this.nameMap.get(methodName);// 如果 attr 不为空说明当前方法配置了事务属性定义,也就是当前方法需要事务管理if (attr != null) {return attr;}else {// look up most specific name matchString bestNameMatch = null;for (Iterator it = this.nameMap.keySet().iterator(); it.hasNext();) {// 判断当前方法是否匹配通配符的方式String mappedName = (String) it.next();if (isMatch(methodName, mappedName) &&(bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {attr = (TransactionAttribute) this.nameMap.get(mappedName);bestNameMatch = mappedName;}}return attr;}
}protected boolean isMatch(String methodName, String mappedName) {return (mappedName.endsWith("*") && methodName.startsWith(mappedName.substring(0, mappedName.length() - 1))) ||(mappedName.startsWith("*") && methodName.endsWith(mappedName.substring(1, mappedName.length())));
}
复制代码
TransactionInterceptor-事务拦截

在完成判断当前方法是否需要事务管理后,如果需要事务管理最终会调用 TransactionInterceptor 执行事务拦截的处理。

public final Object invoke(MethodInvocation invocation) throws Throwable {Class targetClass = (invocation.getThis() != null) ? invocation.getThis().getClass() : null;// if the transaction attribute is null, the method is non-transactional// 获取当前方法所支持的事务配置属性,若不存在则说明当前方法不需要事务管理TransactionAttribute transAtt = this.transactionAttributeSource.getTransactionAttribute(invocation.getMethod(), targetClass);TransactionStatus status = null;TransactionStatus oldTransactionStatus = null;// create transaction if necessaryif (transAtt != null) {        // the transaction manager will flag an error if an incompatible tx already exists// 通过事务管理获取事务,该事务可能是新创建的也可能是当前上下文已存在的事务// 返回事务状态status = this.transactionManager.getTransaction(transAtt);// make the TransactionStatus available to calleesoldTransactionStatus = (TransactionStatus) currentTransactionStatus.get();currentTransactionStatus.set(status);}else {// it isn't a transactional method}Object retVal = null;try {// 目标方法执行retVal = invocation.proceed();}catch (Throwable ex) {// target invocation exceptionif (status != null) {// 异常处理 可能会执行事务的回滚onThrowable(invocation, transAtt, status, ex);}throw ex;}finally {if (transAtt != null) {// use stack to restore old transaction status if one was setcurrentTransactionStatus.set(oldTransactionStatus);}}if (status != null) {// 通过事务管理执行事务提交this.transactionManager.commit(status);}return retVal;
}
复制代码
private void onThrowable(MethodInvocation invocation, TransactionAttribute txAtt,TransactionStatus status, Throwable ex) {// 判断异常是否需要回滚if (txAtt.rollbackOn(ex)) {try {// 通过事务管理执行回滚this.transactionManager.rollback(status);}catch (TransactionException tex) {logger.error("Application exception overridden by rollback exception", ex);throw tex;}}else {// Will still roll back if rollbackOnly is true// 异常不需要回滚的话 则提交事务this.transactionManager.commit(status);}
}
复制代码

TransactionInterceptor 的处理逻辑来看,我们知道其主要做以下事情:

  • 获取当前方法所定义的事务属性
  • 通过事务管理器 Transaction Manager 来获取事务
  • 目标方法执行
  • 执行异常处理,如异常需要回滚则通过事务管理器执行事务 rollback,反之执行事务 commit
  • 方法执行成功则执行事务 commit

也就是说 TransactionInterceptor (事务拦截器) 主要是将事务相关的动作委托给 TransactionManager (事务管理器)处理

TransactionManager-事务管理

本文是以 DataSourceTransactionManager 为例来分析事务的管理实现

getTransaction-获取事务
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {// 获取事务Object transaction = doGetTransaction();if (definition == null) {// 若 definition == null 则采用默认的事务定义definition = new DefaultTransactionDefinition();}// 判断当前上下文是否开启过事务if (isExistingTransaction(transaction)) {// 当前上下文开启过事务// 如果当前方法匹配的事务传播性为 PROPAGATION_NEVER 说明不需要事务则抛出异常if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {throw new IllegalTransactionStateException("Transaction propagation 'never' but existing transaction found");}// 如果当前方法匹配的事务传播性为 PROPAGATION_NOT_SUPPORTED 说明该方法不应该运行在事务中,则将当前事务挂起if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {// 将当前事务挂起Object suspendedResources = suspend(transaction);boolean newSynchronization = (this.transactionSynchronization == SYNCHRONIZATION_ALWAYS);// 返回的事务状态为 不需要事务return newTransactionStatus(null, false, newSynchronization,definition.isReadOnly(), debugEnabled, suspendedResources);}// 如果当前方法匹配的事务传播性为 PROPAGATION_REQUIRES_NEW 表示当前方法必须运行在它自己的事务中;将已存在的事务挂起,重新开启事务if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {if (debugEnabled) {logger.debug("Creating new transaction, suspending current one");}// 挂起当前事务Object suspendedResources = suspend(transaction);// 重新开启个事务doBegin(transaction, definition);boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);// 返回的事务状态为 新建事务return newTransactionStatus(transaction, true, newSynchronization,definition.isReadOnly(), debugEnabled, suspendedResources);}else {boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);// 其他的传播行为 表示在已存在的事务中执行return newTransactionStatus(transaction, false, newSynchronization,definition.isReadOnly(), debugEnabled, null);}}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("Transaction propagation 'mandatory' but no existing transaction found");}// 当前上下文不存在事务// 若传播性为 PROPAGATION_REQUIRED 或 PROPAGATION_REQUIRES_NEW 则开启新的事务执行if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {// 开启新的 connection 并取消自动提交,将 connection 绑定当前线程doBegin(transaction, definition);boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);return newTransactionStatus(transaction, true, newSynchronization,definition.isReadOnly(), debugEnabled, null);}else {// "empty" (-> no) transactionboolean newSynchronization = (this.transactionSynchronization == SYNCHRONIZATION_ALWAYS);// 返回事务状态为 不需要事务return newTransactionStatus(null, false, newSynchronization,definition.isReadOnly(), debugEnabled, null);}
}protected Object doGetTransaction() {// 判断当前线程是否开启过事务if (TransactionSynchronizationManager.hasResource(this.dataSource)) {// 获取当前已存在的 connectoin holderConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);return new DataSourceTransactionObject(holder);}else {return new DataSourceTransactionObject();}
}
复制代码

看到了这里,是不是突然明白 PROPAGATION (事务传播性) 是干什么的了;

简单来说, PROPAGATION 就是为了告诉 Spring 当前方法需不需要事务,是在已存在的事务中执行,还是新开启事务执行;也可以认为是继承上个方法栈的事务,还是拥有自己的事务。

TransactionManager 获取事务的过程实际就是通过当前方法定义的 PROPAGATION (事务传播性) 和当前上下文是否存在事务来判断是否需要事务,是否需要开启新的事务或者是使用当前已存在的事务。

下面看下如何开启新的事务 doBegin

protected void doBegin(Object transaction, TransactionDefinition definition) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;// cache to avoid repeated checksboolean debugEnabled = logger.isDebugEnabled();// 判断 connection holder 是否为空// 两种场景下可能为空:// 1. 上下文不存在事务的时候// 2. 上下文已存在的事务被挂起的时候if (txObject.getConnectionHolder() == null) {if (debugEnabled) {logger.debug("Opening new connection for JDBC transaction");}// 开启新的 connectionConnection con = DataSourceUtils.getConnection(this.dataSource, false);txObject.setConnectionHolder(new ConnectionHolder(con));}Connection con = txObject.getConnectionHolder().getConnection();try {// apply read-onlyif (definition.isReadOnly()) {try {// 如果定义了只读,设置 connection 为只读con.setReadOnly(true);}catch (Exception ex) {// SQLException or UnsupportedOperationExceptionlogger.warn("Could not set JDBC connection read-only", ex);}}// apply isolation level// 设置事务隔离级别if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {txObject.setPreviousIsolationLevel(new Integer(con.getTransactionIsolation()));con.setTransactionIsolation(definition.getIsolationLevel());}// 若 connection 为自动提交则取消if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);con.setAutoCommit(false);}// 设置超时时间if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(definition.getTimeout());}// 将当前 connection holder 绑定到当前上下文TransactionSynchronizationManager.bindResource(this.dataSource, txObject.getConnectionHolder());}catch (SQLException ex) {throw new CannotCreateTransactionException("Could not configure connection", ex);}
}
复制代码

doBegin 执行开启事务的操作,在上下文不存在事务或者上下文事务被挂起的时候会新打开一个 connection, 并按照事务定义设置相关属性,譬如是否只读,取消自动提交,设置事务隔离级别,设置超时时间;最后会将 connection 绑定到当前上下文,也即当前线程。

doSuspend-事务挂起
protected Object doSuspend(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;// 将当前事务的 connection holder 置为空txObject.setConnectionHolder(null);// 并将当前事务与上下文解绑return TransactionSynchronizationManager.unbindResource(this.dataSource);
}
复制代码

事务挂起既是将当前事务的连接持有者清空并与当前上下文解绑,保证后续能够重新开启事务。

数据库操作

针对数据库的操作,本文以 Spring 提供的 jdbcTemplate 工具类进行分析。

public Object execute(final StatementCallback action) {// 若当前需要事务管理的话,那么此时获取的 connection 则是 transaction manager bind 的 connection// 这样就保证数据库操作的时候所获得的的 connection 与 事务管理的一致Connection con = DataSourceUtils.getConnection(getDataSource());Statement stmt = null;// 以下代码省略 此处重点关注如何获取 connection
}
复制代码
public static Connection getConnection(DataSource ds, boolean allowSynchronization)throws CannotGetJdbcConnectionException {// 从当前上下文获取 connection holderConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(ds);if (conHolder != null) {return conHolder.getConnection();}else {try {// 反之新打开一个 connectionConnection con = ds.getConnection();if (allowSynchronization && TransactionSynchronizationManager.isSynchronizationActive()) {logger.debug("Registering transaction synchronization for JDBC connection");// use same Connection for further JDBC actions within the transaction// thread object will get removed by synchronization at transaction completionconHolder = new ConnectionHolder(con);TransactionSynchronizationManager.bindResource(ds, conHolder);TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(conHolder, ds));}return con;}catch (SQLException ex) {throw new CannotGetJdbcConnectionException("Could not get JDBC connection", ex);}}
}
复制代码

从上述代码我们可以看到,当通过 jdbcTemplate 操作数据库时会先从当前上下文中获取 connection; 这样就保证了所获取的事务与事务拦截器的事务为同一个实例,也就是将事务交给了 Spring 来管理。

commit-事务提交
public final void commit(TransactionStatus status) throws TransactionException {DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;// 省略else {try {try {triggerBeforeCommit(defStatus);triggerBeforeCompletion(defStatus);if (status.isNewTransaction()) {logger.info("Initiating transaction commit");// 执行事务提交doCommit(defStatus);}}// 省略}finally {cleanupAfterCompletion(defStatus);}}
}
复制代码

doCommit 执行事务提交

protected void doCommit(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();if (status.isDebug()) {logger.debug("Committing JDBC transaction [" + txObject.getConnectionHolder().getConnection() + "]");}try {// 事务提交txObject.getConnectionHolder().getConnection().commit();}catch (SQLException ex) {throw new TransactionSystemException("Could not commit", ex);}
}
复制代码
resume-事务恢复

从上文的 commit 事务提交操作发现,在完成事务提交之后,还有个后置动作 cleanupAfterCompletion, 该方法会对挂起中的事务执行恢复操作。

private void cleanupAfterCompletion(DefaultTransactionStatus status) {if (status.isNewSynchronization()) {TransactionSynchronizationManager.clearSynchronization();}if (status.isNewTransaction()) {doCleanupAfterCompletion(status.getTransaction());}// 当存在挂起的事务时,执行恢复挂起的事务if (status.getSuspendedResources() != null) {if (status.isDebug()) {logger.debug("Resuming suspended transaction");}resume(status.getTransaction(), status.getSuspendedResources());}
}
复制代码
protected void doResume(Object transaction, Object suspendedResources) {// 将挂起的事务绑定的 connection 重新绑定到当前上下文ConnectionHolder conHolder = (ConnectionHolder) suspendedResources;TransactionSynchronizationManager.bindResource(this.dataSource, conHolder);
}
复制代码

事务的 resume 就是将挂起的事务重新绑定到当前上下文中。

rollback-事务回滚

TransactionInterceptor 调用目标方法执行出现异常的时候,会进行异常处理执行方法 onThrowable

private void onThrowable(MethodInvocation invocation, TransactionAttribute txAtt,TransactionStatus status, Throwable ex) {if (txAtt.rollbackOn(ex)) {try {// 异常需要回滚this.transactionManager.rollback(status);}catch (TransactionException tex) {throw tex;}}else {// 异常不需要回滚的话 则提交事务this.transactionManager.commit(status);}
}
复制代码

onThrowable 方法会通过配置判断当前异常是否需要回滚。

public final void rollback(TransactionStatus status) throws TransactionException {DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;try {try {triggerBeforeCompletion(defStatus);if (status.isNewTransaction()) {// 执行事务回滚logger.info("Initiating transaction rollback");doRollback(defStatus);}else if (defStatus.getTransaction() != null) {if (defStatus.isDebug()) {logger.debug("Setting existing transaction rollback-only");}doSetRollbackOnly(defStatus);}else {logger.info("Should roll back transaction but cannot - no transaction available");}}catch (TransactionException ex) {triggerAfterCompletion(defStatus, TransactionSynchronization.STATUS_UNKNOWN, ex);throw ex;}triggerAfterCompletion(defStatus, TransactionSynchronization.STATUS_ROLLED_BACK, null);}finally {cleanupAfterCompletion(defStatus);}
}protected void doRollback(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();try {// 执行回滚txObject.getConnectionHolder().getConnection().rollback();}catch (SQLException ex) {throw new TransactionSystemException("Could not rollback", ex);}
}
复制代码

小结

此时我们基本明白了 Spring Transaction 的实现原理,下面对其实现做个小结:

  • Spring Transaction 是基于 Spring AOP 的一种实现
  • Spring Transaction 通过配置创建事务 advisor 并创建目标对象代理类
  • 目标方法执行时将会被 TransactionInterceptor 拦截
  • TransactionInterceptor 会委派 TransactionManager 执行事务的创建,事务提交,事务回滚的动作
  • TransactionManager 会根据当前方法配置的事务传播性及当前上下文是否存在事务来判断是否新建事务
  • TransactionManager 当新建事务时会将事务绑定到当前上下文,以保证目标方法执行时获取的事务为同一实例
  • TransactionManager 执行事务挂起时会将当前事务与当前上下文解除绑定关系
  • TransactionManager 执行事务恢复时会将已挂起的事务重新与当前上下文绑定

重拾-Spring Transaction相关推荐

  1. 重拾后端之Spring Boot(二):MongoDB的无缝集成

    重拾后端之Spring Boot(一):REST API的搭建可以这样简单 重拾后端之Spring Boot(二):MongoDb的无缝集成 重拾后端之Spring Boot(三):找回熟悉的Cont ...

  2. Spring Transaction 使用入门 (转)

    Spring Transaction 使用入门 一.开篇陈述 1.1 写文缘由 最近在系统学习spring框架IoC.AOP.Transaction相关的知识点,准备写三篇随笔记录学习过程中的感悟.这 ...

  3. CSS魔法堂:重拾Border之——图片作边框

    前言  当CSS3推出border-radius属性时我们是那么欣喜若狂啊,一想到终于不用再添加额外元素来模拟圆角了,但发现border-radius还分水平半径和垂直半径,然后又发现border-t ...

  4. 重拾算法(3)——用458329个测试用例全面测试二叉树和线索二叉树的遍历算法

    重拾算法(3)--用458329个测试用例全面测试二叉树和线索二叉树的遍历算法 在"上一篇"和"上上一篇"中,我给出了二叉树和线索二叉树的遍历算法.给出算法容易 ...

  5. CSS魔法堂:重拾Border之——更广阔的遐想

    前言  当CSS3推出border-radius属性时我们是那么欣喜若狂啊,一想到终于不用再添加额外元素来模拟圆角了,但发现border-radius还分水平半径和垂直半径,然后又发现border-t ...

  6. 重拾CCNA,学习笔记持续更新ing......(4)

    重拾CCNA,学习笔记持续更新ing......(4) 路由器作用功能的经典解说(笑)(非原创) 假设你的名字叫小不点,你住在一个大院子里,你的邻居有很多小伙伴,在门口传达室还有个看大门的李大爷,李大 ...

  7. reid 数据集 行人重拾别

    行人重拾别 数据集 jacke121 数据集描述 Market-1501 https://blog.csdn.net/ctwy291314/article/details/83544088 数据集描述 ...

  8. spring Transaction Propagation 事务传播

    spring Transaction中有一个很重要的属性:Propagation.主要用来配置当前需要执行的方法,与当前是否有transaction之间的关系. 我晓得有点儿抽象,这也是为什么我想要写 ...

  9. spring Transaction Management --官方

    原文链接:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html 12.  ...

最新文章

  1. oracle 删除补全日志组_浅谈Oracle 20c ASM文件组模板技术
  2. ORA-15260: permission denied on ASM disk group
  3. 编程开发使用的辅助软件大全
  4. python 单行读取文件_python – 在使用for循环读取文件时跳过一行
  5. javascript --- 对象原型
  6. [cb]SceneView 获取鼠标位置
  7. win下MySQL 8.0.11 修改密码、开启远程访问
  8. 2020软考高级系统分析师,你想知道的全在这
  9. CuteFtpnbsp;Pro之站点导入导出
  10. 何同学采访苹果CEO库克上热搜,网友表示自愧不如
  11. 机器学习虾扯淡之线性回归No.39
  12. 加州房价模型(住房价格中位数)
  13. 常见软件设计原则总结
  14. Eclipse中如何查看使用的JDK版本?
  15. av_find_best_stream
  16. 面向对象(封装,this,static,代码块)
  17. 致敬全球第一CEO杰克·韦尔奇,重温其卸任演讲
  18. 用 SimCLR 提高自监督与半监督学习效果
  19. bat批处理文件注释
  20. Cordova iOS 项目中微信/qq/Apple ID插件的安装以及登录的实现

热门文章

  1. Reporting Service 告警w WARN: Thread pool pressure. Using current thread for a work item
  2. Spring MVC ajax:post/get 的具体实现
  3. 深度高能粒子对撞追踪:Kaggle TrackML粒子追踪挑战赛亚军访谈
  4. 数组方法深入扩展(遍历forEach,filter,reduce等)
  5. 《C#多线程编程实战(原书第2版)》——第3章 使用线程池 3.1 简介
  6. 机器学习算法一览,应用建议与解决思路
  7. java中final class的一点思考
  8. CSDN问答频道“华章杯”7月排行榜活动开始,丰厚奖品等你拿
  9. Xcode 编译有错误却没有任何可查看错误的issues
  10. 关于truncate与checkident