事务是逻辑上的一组操作,要么都执行,要么都不执行,关于事务的基本知识可以看我的这篇文章:事务的基础知识
Spring 支持两种方式的事务管理:编程式事务管理、声明式事务管理
TransactionTemplate
或者TransactionManager
手动管理事务,使用较少,但是可以让我们精准控制事务粒度,实现精确的细粒度事务控制@Transactional
注解是最常用的声明式事务使用maven依赖:
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.8version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>5.3.8version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.8version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.25version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.6.9version>
dependency>
实体类:
public class Book {
private int bookId;
private String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
public Book() {
}
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
@Override
public String toString() {
return "Book{" +
"bookId=" + bookId +
", bookName='" + bookName + '\'' +
'}';
}
}
Dao接口类:
import com.jingchuan.transaction.model.Book;
public interface LibraryDao {
/**
* 取得书
* @param name
* @return book
*/
public Book getBook(String name);
/**
* 增加书
* @param book
*/
public void addBook(Book book);
/**
* 删除书
* @param name
*/
public void deleteBook(String name);
}
Dao实现类:
import java.sql.ResultSet;
import java.sql.SQLException;
import com.jingchuan.transaction.model.Book;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
public class LibraryDaoImpl implements LibraryDao {
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public Book getBook(String name) {
String sql = "SELECT * FROM book WHERE bookname=?";
Book mBook = (Book) jdbcTemplate.queryForObject(sql, new Object[]{name}, new RowMapper<Object>() {
@Override
public Object mapRow(ResultSet arg0, int arg1) throws SQLException {
Book book = new Book();
book.setBookId(arg0.getInt("bookId"));
book.setBookName(arg0.getString("bookName"));
return book;
}
});
return mBook;
}
@Override
public void addBook(Book book) {
String sql = "INSERT INTO book VALUES(?,?)";
jdbcTemplate.update(sql, book.getBookId(), book.getBookName());
// jdbcTemplate.update(sql, book.getBookId(), book.getBookName());
}
@Override
public void deleteBook(String name) {
String sql = "DELETE FROM book WHERE bookname=?";
jdbcTemplate.update(sql, name);
}
}
transaction.xml 配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
bean>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
bean>
<bean id="libraryFactory" class="com.jingchuan.transaction.dao.LibraryDaoImpl"
p:jdbcTemplate-ref="jdbcTemplate" />
<aop:config>
<aop:pointcut id="serviceMethod"
expression="execution(* com.jingchuan.transaction.dao.LibraryDaoImpl.*(..))" />
<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" />
aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="add*" rollback-for="Exception" propagation="REQUIRED"/>
<tx:method name="del*" />
tx:attributes>
tx:advice>
beans>
测试类:
import com.jingchuan.transaction.dao.LibraryDao;
import com.jingchuan.transaction.model.Book;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestTransaction {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:transaction.xml");
Book book1 = new Book(1, "西游记");
Book book2 = new Book(2, "红楼梦");
Book book3 = new Book(3, "金瓶梅");
Book book4 = new Book(4, "三国演义");
Book book5 = new Book(5, "水浒传");
LibraryDao libraryDaoImpl = (LibraryDao) ctx.getBean("libraryFactory");
libraryDaoImpl.addBook(book2);
System.out.println(libraryDaoImpl.getBook("西游记"));
}
}
需要自己创建一个数据库并创建Book表,bookId和bookName两个字段:
CREATE TABLE `book` (
`bookId` int(11) NOT NULL,
`bookName` varchar(255) DEFAULT NULL,
PRIMARY KEY (`bookId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
根据IOC和AOP的知识,应该了解到上面的例子中,transaction.xml
中的每个标签都会被加载成一个个Bean定义和Bean实例存储在spring工厂和容器中,对于xml配置中的前面部分的标签这里就不再一一跟进了,不了解的同学可以去看我之前的IOC源码详解和AOP源码分析,我们直接来到tx:advice
标签的解析org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
//自定义标签解析 我们的tx:advice标签就是事务自定义标签
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
根据自定义标签解析的过程,我们看:
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 这个namespaceUri 在tx标签解析时值为:http://www.springframework.org/schema/tx
// 因为xml配置中顶部beans xmlns配置了tx标签的解析规范
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 根据namespaceUri 匹配他的标签处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 根据匹配到的处理器进行标签解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
在上面这段代码中,this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
这一步就是根据namespaceUri
的值去找到它对应的处理类,spring-tx
包在META-INF\spring.handlers
中配置了
http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
所以在resolve(namespaceUri)
这一步会调用org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#resolve
,然后实例化org.springframework.transaction.config.TxNamespaceHandler
并调用他的init
方法,这个就不贴源代码了,这一步不复杂,我们看TxNamespaceHandler
的init
方法:
public void init() {
registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
}
可以看到这个init
方法里面定义了tx标签下的每个子标签名称对应的解析器,后续事务相关的配置就根据这个定义进行解析的。这个和aop的其实是同一个套路。
然后回到:org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
方法,看最后一步,根据拿到的TxNamespaceHandler
调用其parse
方法,这一步会先调用父类的org.springframework.beans.factory.xml.NamespaceHandlerSupport#parse
方法,根据配置,我们第一个解析的是tx:advice
标签,所以这里就是获取advice
标签的解析器,也就是上面init
方法中定义的TxAdviceBeanDefinitionParser
类,所以这个parse
最终会来到TxAdviceBeanDefinitionParser
的父类AbstractBeanDefinitionParser
的parse
方法,这个方法内部的解析逻辑这里就不一一跟进了,最终就是把我们配置里面的内容解析成BeanDefinition
,这个BeanDefinition
实际是TransactionInterceptor
的定义,他的Bean name是txAdvice
。
…
然后略过其他的步骤来到org.springframework.context.support.AbstractApplicationContext#refresh
方法的finishBeanFactoryInitialization(beanFactory);
这一步是实例化所有单例Bean,当实例化我们xml配置中的libraryFactory
时,发现他符合我们AOP代理的配置规则,所以会给他创建代理对象,那么这个代理对象的的目标对象就是LibraryDaoImpl
,切入点(pointcut)就是配置中的com.jingchuan.transaction.dao.LibraryDaoImpl.*(..)
,他的通知行为(advice)就是org.springframework.transaction.interceptor.TransactionInterceptor
,然后JdkDynamicAopProxy
根据这些创建代理类并放入Spring容器中。关于AOP拦截Bean创建并创建代理的过程,大家自行了解AOP源码。
那么至此,Spring加载配置文件中的Bean定义和根据定义创建实例就算完成了。
然后来到测试类中调用libraryDaoImpl.addBook(book2)
,那么我们上面分析过了,libraryDaoImpl
这个实例其实是JdkDynamicAopProxy
创建的代理类代理的,所以会进入org.springframework.aop.framework.JdkDynamicAopProxy#invoke
反射执行目标代码,经过代理类处理反射会来到:org.springframework.transaction.interceptor.TransactionInterceptor#invoke
方法
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
@Override
@Nullable
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
@Override
public Object getTarget() {
return invocation.getThis();
}
@Override
public Object[] getArguments() {
return invocation.getArguments();
}
});
}
这里就是真正进入Spring事务的代码了
上面我们知道进入了org.springframework.transaction.interceptor.TransactionInterceptor#invoke
方法,所以我们直接进入看org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
:
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);
// 根据事务属性信息获取事务管理器 这里我们配置的是DataSourceTransactionManager
final TransactionManager tm = determineTransactionManager(txAttr);
// 判断是不是 响应式/反应式 事务
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
boolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);
boolean hasSuspendingFlowReturnType = isSuspendingFunction &&
COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName());
if (isSuspendingFunction && !(invocation instanceof CoroutinesInvocationCallback)) {
throw new IllegalStateException("Coroutines invocation not supported: " + method);
}
CoroutinesInvocationCallback corInv = (isSuspendingFunction ? (CoroutinesInvocationCallback) invocation : null);
ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
Class<?> reactiveType =
(isSuspendingFunction ? (hasSuspendingFlowReturnType ? Flux.class : Mono.class) : method.getReturnType());
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(reactiveType);
if (adapter == null) {
throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
method.getReturnType());
}
return new ReactiveTransactionSupport(adapter);
});
InvocationCallback callback = invocation;
if (corInv != null) {
callback = () -> CoroutinesUtils.invokeSuspendingFunction(method, corInv.getTarget(), corInv.getArguments());
}
Object result = txSupport.invokeWithinTransaction(method, targetClass, callback, txAttr, (ReactiveTransactionManager) tm);
if (corInv != null) {
Publisher<?> pr = (Publisher<?>) result;
return (hasSuspendingFlowReturnType ? KotlinDelegate.asFlow(pr) :
KotlinDelegate.awaitSingleOrNull(pr, corInv.getContinuation()));
}
return result;
}
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// 获取目标方法唯一标识 在我们例子中是:com.jingchuan.transaction.dao.LibraryDaoImpl.addBook
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
//判断是不是声明式事务
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 如果需要那就创建事务获取TransactionInfo
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
// 调用真实的目标方法 也就是我们的addBook方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
// 如果发生异常,那么处理异常之后的逻辑
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 清理事务
cleanupTransactionInfo(txInfo);
}
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
// 提交事务 这个只有在没发生异常的时候才会执行到这里 发生异常的时候就throw了
commitTransactionAfterReturning(txInfo);
return retVal;
}
//编程式事务
else {
Object result;
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
try {
Object retVal = invocation.proceedWithInvocation();
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
return retVal;
}
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 {
// 执行完成后从ThreadLocal中去掉本次的事务信息
cleanupTransactionInfo(txInfo);
}
});
}
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;
}
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
}
上面这个方法就是真正的事务处理逻辑了,我们主要来看声明式事务的处理逻辑,分开来看
createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
completeTransactionAfterThrowing(txInfo, ex);
commitTransactionAfterReturning(txInfo);
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");
}
}
}
// 把获取的事务包装成TransactionInfo并绑定到ThreadLocal
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
进入tm.getTransaction(txAttr);
看源码
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
// Use defaults if no transaction definition given.
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
// doGet开头的方法 肯定就是创建并返回事务了
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
// 判断上面transaction 是不是已有的事务
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
// 根据已有事物的传播行为进行对应处理
return handleExistingTransaction(def, transaction, debugEnabled);
}
// 能走到这里说明就不存在已有的事务 往下的处理逻辑就是针对没有已创建事务的处理
// Check definition settings for new transaction.
// 检查事务的超时时间是否设置正确
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
// 如果事务定义为MANDATORY,那这时候必须在一个已有事务里面运行,走到这里说明没有已有的事务,就会报错
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
// 如果事务定义为REQUIRED或者REQUIRES_NEW或者NESTED
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 空挂起 就是不做任何操作
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
// 开启一个事务
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + def);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
上面这个方法中,我们重点看三个方法:
doGetTransaction()
handleExistingTransaction(def, transaction, debugEnabled)
startTransaction(def, transaction, debugEnabled, suspendedResources)
因为我们例子中使用的是DataSourceTransactionManager
,所以自然会调用到org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction
:
protected Object doGetTransaction() {
// 创建一个DataSourceTransactionObject
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
// 设置本次事务中是否允许保存点, isNestedTransactionAllowed() 为true
txObject.setSavepointAllowed(isNestedTransactionAllowed());
// 这里是根据数据库链接属性去当前线程中获取缓存,如果当前线程中不存在,那么就返回null
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
最终返回一个DataSourceTransactionObject
再回到org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
继续跟进
根据上面的分析我们知道这个方法是处理在当前线程中已存在事务时的处理流程
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
// 如果是事务定义是NEVER 以非事务方式执行,如果当前存在事务,则抛出异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
// 如果当前事务定义是NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
// 挂起当前事务
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
// 创建新的TransactionStatus
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
// 如果当前事务定义为REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
// 挂起当前事务
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
//开启新事务
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
// 如果当前事务定义为NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作,这个是事务的嵌套
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 是否允许嵌套事务 不允许的话直接抛出异常
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
// 是否为嵌套事务使用保存点
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
// 开启新事务
return startTransaction(definition, transaction, debugEnabled, null);
}
}
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 创建一个DefaultTransactionStatus 记录了事务的各种属性信息
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 开启事务链接 如果是个新事务 那么绑定到当前线程
doBegin(transaction, definition);
//新同步事务的设置
prepareSynchronization(status, definition);
return status;
}
再看doBegin(transaction, definition);
方法,因为我们使用DataSourceTransactionManager
所以就到了org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// 获取链接
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
// 设置为不自动提交 这就是事务开启的原因 让他不自动提交 交由我们自己手动提交事务
con.setAutoCommit(false);
}
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()) {
// 如果是个新事务,那么把这个事务已链接信息为key 事务链接为value 绑定到当前线程
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);
}
}
那到这里,事务创建的过程就分析完成了,然后我们回到org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
方法,我们刚才基本的过了一遍createTransactionIfNecessary(ptm, txAttr, joinpointIdentification)
这一步,最终是根据我们当前的事务状态和传播行为等信息,得到了当前事务的TransactionInfo
然后接着就是反射执行目标方法,也就是我们例子中的addBook
执行完成后根据是否发生异常来处理后续流程,比如异常回滚或者没有异常的时候正常提交事务。
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;
}
}
}
}
其实上面这段代码就是根据我们的配置来处理发生异常的时候,需不需要回滚事务或者提交事务。
其中txInfo.transactionAttribute.rollbackOn(ex)
这个就是用来判断是不是需要回滚的重要逻辑,他向下的实际调用链是:
org.springframework.transaction.interceptor.DelegatingTransactionAttribute#rollbackOn
抽象类委托处理org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn
异常规则匹配具体实现org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn
如果规则匹配没有匹配到,那么走这个默认的我们具体看第二步的规则匹配:
public boolean rollbackOn(Throwable ex) {
if (logger.isTraceEnabled()) {
logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
}
RollbackRuleAttribute winner = null;
int deepest = Integer.MAX_VALUE;
if (this.rollbackRules != null) {
// 这个this.rollbackRules 就是我们配置的具体的异常
// 比如@Transactional的rollbackFor和noRollbackFor 他们都是RollbackRuleAttribute
// 只不过区分为RollbackRuleAttribute 和 NoRollbackRuleAttribute 也就是回滚的异常和不会滚的异常
for (RollbackRuleAttribute rule : this.rollbackRules) {
// 循环进行深度匹配 其实就是看异常是不是和配置的异常匹配 并从子类往上级的父类进行追踪
// 每次都是先匹配回滚的 再匹配不会滚的 同时从子类开始匹配(子类优先)
int depth = rule.getDepth(ex);
if (depth >= 0 && depth < deepest) {
// 如果匹配到 那么就设置匹配成功的记录
deepest = depth;
winner = rule;
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Winning rollback rule is: " + winner);
}
// User superclass behavior (rollback on unchecked) if no rule matches.
if (winner == null) {
// 如果没匹配到 那么使用默认的规则 也就是DefaultTransactionAttribute的rollbackOn
logger.trace("No relevant rollback rule found: applying default rules");
return super.rollbackOn(ex);
}
// 最后再看匹配到的是不是NoRollbackRuleAttribute(不需要回滚的) 如果是那么就不会滚
return !(winner instanceof NoRollbackRuleAttribute);
}
上面这个方法就是具体看会不会回滚的重点,其中深度匹配的逻辑大家可以断点再详细看看。
然后如果我们平时使用的@Transactional
注解 不写rollbackFor = Exception.class
,那么他就只会捕捉到RuntimeException
或者Error
,因为在默认的DefaultTransactionAttribute
匹配规则中是:
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
所以大家在使用@Transactional
注解的时候要注意异常的时候是否可以回滚。同时配置了rollbackFor
和noRollbackFor
属性的时候要注意匹配的规则是有优先级的,尤其是rollbackFor
和noRollbackFor
的异常类有父子级关系的时候。 实再不确定的时候就在这里断点看一看
目标方法正常执行完成后就会执行提交事务
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());
}
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
这里的commit
会来到:org.springframework.transaction.support.AbstractPlatformTransactionManager#commit
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()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus, false);
return;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus, true);
return;
}
// 执行提交
processCommit(defStatus);
}
跟进到:org.springframework.transaction.support.AbstractPlatformTransactionManager#processCommit
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
// 模板方法扩展
prepareForCommit(status);
// 提交前的各种扩展处理
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
// 如果是一个新事务 在我们例子中会来到这里
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
// 执行提交
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException | Error ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
// 处理提交后的 扩展/拦截 处理
triggerAfterCommit(status);
}
finally {
// 提交之后
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
// 清楚事务的各种本地线程数据 包括设置为自动提交等
cleanupAfterCompletion(status);
}
}
再看:doCommit(status);
,因为我们使用的是DataSourceTransactionManager
,所以会来到org.springframework.jdbc.datasource.DataSourceTransactionManager#doCommit
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
// 获取链接
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
// 提交事务
con.commit();
}
catch (SQLException ex) {
throw translateException("JDBC commit", ex);
}
}
到这里就把Spring事务基本流程分析了一遍,过程中有很多地方一笔带过了,大家自己跟着断点再细看吧,全部写的话太多了写不完,过程中有不正确的欢迎指正。