1:springBoot开启事务
只要在启动类上加上该注解,就可以表示启用事务了
@EnableTransactionManagement
@SpringBootApplication
public class NettyChatApplication {
public static void main(String[] args) {
SpringApplication.run(NettyChatApplication.class, args);
}
}
//只要在需要使用事务的方法上加上 注解即可
既然开启事务和使用事务是二个注解,那么只能从这二个注解开始入手了。先看下 @Transactional 注解 ,发现什么都没有,只有一些注解该有的标志信息
所以现在只能靠 @EnableTransactionManagement 注解了,该注解中有一个 @Import注解,这个注解的作用就是将该注解中的Bean注入到Spring容器中,也就是这里的 TransactionManagementConfigurationSelector.class, 所以我们进入这个类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
这里重点关注 ProxyTransactionManagementConfiguration.class,进入这个类
这个类是一个配置类,里面注入了很多Bean,包括这里的一个拦截器 ,这个拦截器的作用就是当执行的方法上有 @Transactional 注解,就会经过该拦截器拦截并进行处理,你可以在该拦截器中的 invoke() 方法上打上一个断点,然后执行一个加 @Transactional 注解的方法,程序就会停在断点上,如下图
所以我们要分析Spring事务的原理的入口就是在该拦截器的 invoke() 方法
2:事务原理分析入口
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// 事务的入口方法
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
我们进入该方法,这个方法我只把一些核心方法截取出来了
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
//获取@Trancation注解中的一些信息,比如该注解配置的超时时间,事务隔离级别等一些信息
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final TransactionManager tm = determineTransactionManager(txAttr);
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 开始创建事务
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
//执行自己的业务逻辑
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 有异常就回滚
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
// 事务提交
commitTransactionAfterReturning(txInfo);
return retVal;
}
}
3:开始创建事务
开始创建事务是在这一行代码,进入该方法
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// 这里设置事务的一些名字之类的信息,无关的代码
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 核心方法:去获取一个事务
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
//组装该事务的一些信息,然后返回
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
我们只需要去分析获取事务的方法就行
AbstractPlatformTransactionManager.class
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
// 尝试去获取一个事务,第一次进来肯定是空的,这里就涉及到事务的传播机制了
// 这种情况一般是在一个类中,一个方法调用了另外一个方法,二个方法都有@Transaction注解,
// 此时执行到另外一个方法的时候,判断该方法上的事务隔离级别,根据不同的事务隔离级别来判断
// 是继续使用该事务还是创建一个新的事务
// 现在是第一次进来,所以这里肯定是为空的
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
if (isExistingTransaction(transaction)) {
// 第一次进来的时候是没有事务的,所以不会走到这里来
return handleExistingTransaction(def, transaction, debugEnabled);
}
// 如果设置的超时时间小于-1,直接抛出异常
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
// 如果事务的传播机制是: PROPAGATION_MANDATORY抛出异常
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
//如果是以下几种传播机制就开启一个事务
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
try {
// 核心方法:开启一个事务
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
}
默认的传播机制是 Propagation.REQUIRED,所以会进入 startTransaction(def, transaction, debugEnabled, suspendedResources); 这个方法
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 核心方法:开启事务
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;
Connection con = null;
try {
//获取一个数据库的连接
con = txObject.getConnectionHolder().getConnection();
// 设置是否只读
txObject.setReadOnly(definition.isReadOnly());
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
// 设置自动提交为false
con.setAutoCommit(false);
}
this.prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = this.determineTimeout(definition);
if (timeout != -1) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());
}
}
}
总结:假设你现在项目整合的是MyBatis,然后该方法操作了不同的数据表,MyBaitis首先会获取一个SqlSession,也就是一个数据库连接,这里操作二次数据库,所以会获取二次的数据库连接。而Spring的事务是依赖MySql事务的。
MySql中,我们开启一个事务是 beging -> 执行SQL -> commit/rollback。整个事务内都是在一个数据库连接中完成的,所以Spring要让事务有效,所有的数据库操作都必须在同一个数据库连接中完成。所以在开启一个事务这里,需要单独获取一个数据库连接,然后,后续的数据库操作都是用该数据库连接了,MyBatis不会再重新去获取一个数据库连接去操作数据了
// 更新用户信息
userMapper.updateById(user);
//更新班级信息
classMapper.updateById(1);
所以我们就可以知道在开启事务就是获取一个新的数据库连接,设置自动提交为false,然后设置一些超时时间等等,该连接用于后续该事务中所有的数据库操作
4: 执行自己的业务逻辑
retVal = invocation.proceedWithInvocation();
这里就是执行我们的业务逻辑了,如果有关于数据库的操作,都是使用上一步获取的数据路连接去操作数据了,而不是重新去获取一个新的数据库连接
5:事务提交
commitTransactionAfterReturning(txInfo);
我们先看事务提交的逻辑
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());
}
}
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);
}
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
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);
}
}
}
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
//通过事务上下文拿到开启事务创建的那个数据库连接
Connection con = txObject.getConnectionHolder().getConnection();
try {
// 执行提交
con.commit();
} catch (SQLException var5) {
throw new TransactionSystemException("Could not commit JDBC transaction", var5);
}
}
事务提交:就是通过事务上下文拿到开启事务那一步创建的数据库连接,然后执行commit操作,就相当于我们在MySql客户端执行的 commit 是一样的。
5:事务回滚
completeTransactionAfterThrowing(txInfo, ex);
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
// txInfo.transactionAttribute.rollbackOn(ex)这里会判断异常类型,如果不匹配就不会回滚,就会走到下面的else分支,去执行提交的操作
// 事务无效的一个原因之一:就是捕获的异常不匹配
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 执行事务回滚操作
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
}
else {
// 异常不匹配就会执行事务提交操作
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
}
}
进入回滚操作
@Override
public final void rollback(TransactionStatus status) throws TransactionException {
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
//事务回滚
processRollback(defStatus, false);
}
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
triggerBeforeCompletion(status);
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
// 事务回滚
doRollback(status);
}
}
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
//通过事务上下文拿到开启事务创建的那个数据库连接
Connection con = txObject.getConnectionHolder().getConnection();
try {
// 执行回滚
con.rollback();
} catch (SQLException var5) {
throw new TransactionSystemException("Could not roll back JDBC transaction", var5);
}
}
事务回滚:就是通过事务上下文拿到开启事务那一步创建的数据库连接,然后执行rollback操作,就相当于我们在MySql客户端执行的 rollback 是一样的。
6事务流程总结
-
1:开启事务:获取一个新的数据库连接,然后设置该连接的自动提交为false,然后设置一些别的信息,比如超时时间等等
-
2:执行自己业务逻辑:拿到上一步获取到的数据库连接,去执行数据库的操作
-
3:如果没有异常,就通过事务的上下文拿到第一步获取的数据库连接,然后执行提交操作
-
4:如果有异常,首先判断异常类型是否匹配,如果匹配则通过事务上下文拿到第一步获取的数据库连接,然后执行回滚操作,如果不匹配就执行事务提交
在这里只是事务的一个最简单的流程,还有很多点是没有分析到的,比如在开启事务那里,如果已经存在事务了,就会进入另外一个处理逻辑,当然还有事务的挂起,事务的恢复等等,但是只要把整体的流程弄清楚了,这些分支就可以慢慢的去研究了
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
// 尝试去获取一个事务,第一次进来肯定是空的,这里就涉及到事务的传播机制了
// 这种情况一般是在一个类中,一个方法调用了另外一个方法,二个方法都有@Transaction注解,
// 此时执行到另外一个方法的时候,判断该方法上的事务隔离级别,根据不同的事务隔离级别来判断
// 是继续使用该事务还是创建一个新的事务
// 现在是第一次进来,所以这里肯定是为空的
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
if (isExistingTransaction(transaction)) {
// 第一次进来的时候是没有事务的,所以不会走到这里来
return handleExistingTransaction(def, transaction, debugEnabled);
}
- 我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。