Spring事务二
尽管上面分析过了spring事务,但是感觉还是不够。那么来分析一下具体的信息。这里将会更加深入代码层面来研读这里的信息。
也会搭配spring集合mybatis中的事务来进行说明。
TransactionSynchronizationManager
首先看下TransactionSynchronizationManager,这个类在源码中有大量的应用。重点代码分布在两个区域
doBegin和prepareSynchronization方法中。
看下doBegin,这里是创建数据库连接、开启事务的地方
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
如果是新创建的数据库连接,那么代码执行到这里的时候,就会将当前的数据库连接存入到ThreadLocal中来,看下具体的逻辑:
public static void bindResource(Object key, Object value) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Assert.notNull(value, "Value must not be null");
Map<Object, Object> map = resources.get();
// set ThreadLocal Map if none found
if (map == null) {
map = new HashMap<>();
resources.set(map);
}
Object oldValue = map.put(actualKey, value);
// Transparently suppress a ResourceHolder that was marked as void...
if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
oldValue = null;
}
if (oldValue != null) {
throw new IllegalStateException(
"Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread");
}
}
看下对应的resources的底层数据结构
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
这里的map中的key当前存的是datasource,对应的value是ConnectionHolder;那么也就是说将数据源和当前的数据库连接保存到当前线程中来。
但是这里只有开启了事务的情况下,那么在外部可以通过TransactionSynchronizationManager来获取得到这里的datasource和数据库连接。
总之TransactionSynchronizationManager可以获取得到事务的很多信息。
那么接着看下prepareSynchronization
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
TransactionSynchronizationManager.initSynchronization();
}
}
将当前事务的激活状态、隔离级别、是否只读、事务名称、事务的同步调用器保存在了当前线程中来。
在加了@Transactional事务的方法中可以使用TransactionSynchronizationManager来获取得到对应的信息。
这里来放入对应的测试代码:
@Service
public class UserService2 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private UserService2 userService;
@Transactional
public void inert() {
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void suspend() {
String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
System.out.println("当前事务被挂起了,执行这里的代码,当前事务的名称是:"+currentTransactionName);
}
});
userService.update();
}
/**
* newTransaction
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void update() {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void beforeCompletion() {
String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
System.out.println("当前事务回滚之前需要进行的操作"+currentTransactionName);
}
});
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
throw new NullPointerException();
}
}
在inset方法中,在事务被挂起时执行对应的代码,在update方法中,在事务回滚之前打印这里的代码。
打印事务名称相同
所以在这里首先看下一个有趣的现象:
@Service
public class UserService1 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private UserService1 userService;
@Transactional
public void inert() {
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
userService.update();
}
@Transactional
public void update() {
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
// throw new NullPointerException();
}
}
打印的两个方法的事务名称是一样的!!按照上面的逻辑来说,不同的事务名称不应该是一样的吗。
重点在于update方法在创建事务的时候:prepareSynchronization
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
TransactionSynchronizationManager.initSynchronization();
}
}
这里的if判断是不成立的。那么为什么这里的status.isNewSynchronization()为FALSE呢,那么看下status是怎么来进行构建的。
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
对于第一行代码来说,是为true的。但是对于构造中的代码:
protected DefaultTransactionStatus newTransactionStatus(
TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,
boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {
// 这里因为当前事务正在活跃中,所以最终为FALSE
boolean actualNewSynchronization = newSynchronization &&
!TransactionSynchronizationManager.isSynchronizationActive();
return new DefaultTransactionStatus(
transaction, newTransaction, actualNewSynchronization,
definition.isReadOnly(), debug, suspendedResources);
}
suspendedResources
表示的挂起的资源,其实本质上就是数据库连接。
看下代码使用的地方:AbstractPlatformTransactionManager.getTransaction
SuspendedResourcesHolder suspendedResources = suspend(null);
继续进去:
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
// 如果再次之前没有事务,那么这里肯定为FALSE;如果之前开启过事务,那么这里为true。
if (TransactionSynchronizationManager.isSynchronizationActive()) {
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
suspendedResources = doSuspend(transaction);
}
String name = TransactionSynchronizationManager.getCurrentTransactionName();
TransactionSynchronizationManager.setCurrentTransactionName(null);
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
TransactionSynchronizationManager.setActualTransactionActive(false);
return new SuspendedResourcesHolder(
suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
}
catch (RuntimeException | Error ex) {
// doSuspend failed - original transaction is still active...
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
}
else if (transaction != null) {
// Transaction active but no synchronization active.
Object suspendedResources = doSuspend(transaction);
return new SuspendedResourcesHolder(suspendedResources);
}
else {
// Neither transaction nor synchronization active.
return null;
}
}
如果为FALSE,那么这里什么都不做返回null。
如果为true,那么这里在做什么?
private List<TransactionSynchronization> doSuspendSynchronization() {
List<TransactionSynchronization> suspendedSynchronizations =
TransactionSynchronizationManager.getSynchronizations();
for (TransactionSynchronization synchronization : suspendedSynchronizations) {
synchronization.suspend();
}
TransactionSynchronizationManager.clearSynchronization();
return suspendedSynchronizations;
}
首先将当前线程中的资源挂起,然后清空;
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
txObject.setConnectionHolder(null);
return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
将当前数据库连接置为空,然后在当前线程中解除绑定,这里返回的就是数据库连接的包装类。
然后获取得到当前事务的信息保存到了SuspendedResourcesHolder对象中来,而SuspendedResourcesHolder又会被存到DefaultTransactionStatus中
然后调用prepareTransactionInfo方法,存入到本地线程中去:
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, String joinpointIdentification,
@Nullable TransactionStatus status) {
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
// We need a transaction for this method...
// The transaction manager will flag an error if an incompatible tx already exists.
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.
txInfo.bindToThread();
return txInfo;
}
封装成为一个TransactionInfo粗惹怒到当前线程中来:
private void bindToThread() {
// Expose current TransactionStatus, preserving any existing TransactionStatus
// for restoration after this transaction is complete.
// 将之前的TransactionInfo保存
this.oldTransactionInfo = transactionInfoHolder.get();
// 将新的TransactionInfo设置到当前线程中来
transactionInfoHolder.set(this);
}
这个transactionInfo有什么用呢?这涉及到事务传播机制。
也就是判断事务是否存在的地方:
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(def, transaction, debugEnabled);
}
从注释中可以看到检查传播行为在这里如何来进行决定。而判断是否存在事务的标记是什么?
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
判断数据库连接是否存在,如果存在还需要判断之前的事务是否是激活的。如果是激活的,那么当前事务决定怎么样来处理之前的事务呢?
看对应的处理方式,看下最普通的方式ISOLATION_DEFAULT:
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
这里直接开始着手构建事务状态对象:
protected final DefaultTransactionStatus prepareTransactionStatus(
TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,
boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, newTransaction, newSynchronization, debug, suspendedResources);
prepareSynchronization(status, definition);
return status;
}
而这里又将回到之前的,注意一个判断:newSynchronization,这里会为FALSE,因为下面的代码
boolean actualNewSynchronization = newSynchronization &&
!TransactionSynchronizationManager.isSynchronizationActive();
返回之后,着手来构建一个新的TransactionInfo对象人,然后将其绑定到线程上去,但是也会将旧的TransactionInfo对象绑定到新的TransactionInfo对象上
private void bindToThread() {
// Expose current TransactionStatus, preserving any existing TransactionStatus
// for restoration after this transaction is complete.
this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this);
}
那么这里首先来看下第二个事务的正常提交
无论是在提交还是回滚之前,首先会执行一行代码:
cleanupTransactionInfo(txInfo);
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
if (txInfo != null) {
txInfo.restoreThreadLocalStatus();
}
}
然后继续看:
private void restoreThreadLocalStatus() {
// Use stack to restore old transaction TransactionInfo.
// Will be null if none was set.
transactionInfoHolder.set(this.oldTransactionInfo);
}
将旧的transactionInfo重新绑定到线程上去。然后执行提交,也就是说这里会将所有的属于旧事务的信息都封装起来。
public final void commit(TransactionStatus status) throws TransactionException {
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 是否局部有回滚标记
if (defStatus.isLocalRollbackOnly()) {
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 {
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");
}
}
// 真正有用的代码
// 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);
}
}