spring的核心逻辑是通过TransactionInterceptor来代理对应的方法,并根据TransactionInfo来维护当前方法下的对应的事务状态信息。下图展示了其核心逻辑。
其对应的代码为:
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, 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.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
可以看到其首先是获取一个TransactionInfo,这个TransactionInfo主要是先从TransactionAttributeSource中获取TransactionAttribute,然后再根据这个TransactionAttribute获取PlatformTransactionManager,而PlatformTransactionManager则根据TransactionAttribute已经线程上下文中的数据生成当前的TransactionStatus数据,最后根据TransactionAttribute,TransactionStatus,PlatformTransactionManager共同组成TransactionInfo,并将其存入到线程上下文中。
TransactionAttributeSource主要作用是提供TransactionAttribute,这个接口的方法也只有一个即为:**TransactionAttribute getTransactionAttribute(Method method, Class> targetClass);**不过其获取的方式不同而有不同的实现。
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" propagation="SUPPORTS" isolation="DEFAULT"
read-only="true" />
</tx:attributes>
</tx:advice>
MatchAlwaysTransactionAttributeSource: 这个TransactionAttributeSource所有的method都是返回的同一套的TransactionAttribute。
AnnotationTransactionAttributeSource: 其继承了AbstractFallbackTransactionAttributeSource,这个类主要是以[method,class]为key对其transactionAttribute做了缓存。其主要逻辑是根据注册的TransactionAnnotationParser找出对应的transactionAttribute,TransactionAnnotationParser主要有三种:
TransactionInterceptor是事务的核心类,其是一个实现了MethodInterceptor接口的类,其在spring容器中是以Advice的形式通过AOP到目标方法的代理调用链上,并在调用时进行相应的事务处理。对于TransactionInterceptor这个Advice的注入方式主要有两种,第一种是
而
其主要逻辑刚开始描述过,先生成一个TransactionInfo并通过ThreadLocal的方式绑定到当前线程以便外部代码获取当前的TransactionStatus数据。并根据执行成功或者失败从当前的TransactionInfo中获取PlatformManager和TransactionStatus进行commit或者rollack操作。
PlatformTransactionManager是spring事务的核心,如果不使用代理式的事务处理,可以直接使用这个类进行事务处理,其提供的主要方法是getTransaction,commit和rollback这三个方法,其主要作用是
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
在介绍三个方法之前,先介绍几个主要的方法suspend,resume,和savepoint和transaction这几件事。
下面来看一下这三个方法的代码
@Override
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
}
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// Check definition settings for new transaction.
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException ex) {
resume(null, suspendedResources);
throw ex;
}
catch (Error err) {
resume(null, suspendedResources);
throw err;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + definition);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
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 {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
catch (Error beginErr) {
resumeAfterBeginException(transaction, suspendedResources, beginErr);
throw beginErr;
}
}
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.
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
// 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);
}
可以看到getTransaction方法首先是从子类中获取transaction对象,并然后分为两种情况以及不同的传播行为进行了不同的操作,下面表格则列出了在当前有transaction和没有transaction的情况下每种传播行为其对应的不同操作。
名称 | 创建新的Transaction | 创建savePoint | 报错 | 挂起当前transaction |
---|---|---|---|---|
Required | 否 | 否 | 否 | 否 |
Requires_new | 是 | 否 | 否 | 是 |
Supports | 否 | 否 | 否 | 否 |
nested | 否 | 是 | 否 | 否 |
never | 否 | 否 | 是 | 否 |
not_supported | 否 | 否 | 否 | 是 |
mandatory | 否 | 否 | 否 | 否 |
名称 | 创建新的Transactiton | 报错 | 挂起 |
---|---|---|---|
required | 是 | 否 | 是 |
requeires_new | 是 | 否 | 是 |
supports | 否 | 否 | 否 |
not_supported | 否 | 否 | 否 |
nested | 否 | 否 | 是 |
never | 否 | 否 | 否 |
mandatory | 否 | 是 | 否 |
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
boolean globalRollbackOnly = false;
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
doCommit(status);
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (globalRollbackOnly) {
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 ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
catch (Error err) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, err);
throw err;
}
// 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);
}
}
private void processRollback(DefaultTransactionStatus status) {
try {
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);
}
else if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
}
catch (RuntimeException ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
catch (Error err) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw err;
}
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
}
finally {
cleanupAfterCompletion(status);
}
}
可以看到commit和rollback的主要操作时看有没有savePoint和是否为newTransaction,对于commit操作,可以看到当有savePoint,其最终只是清除当前的savePoint,并且只有当前的transaction是newTransaction(对于DatasourcePlatformMananager即为创建connection的方法)才进行真正的commit的操作。对于rollback则是对于有savePoint则是回滚到savePoint,也只是newTransaction进行真正的rollback操作,其他情况则是将其标记为rollback等待创建这个transaction的方法进行真正的rollback操作。
DataSourceTransactionManager是继承了AbstractPlatformTransactionManager,是我们常用的对于事务配置用的platformTransactionManager。
而TransactionSynchronizationManager则是一个维护了一系列ThreadLocal的将数据绑定到线程的类。
DataSourcetransactionManager将当前线程下的连接以ConnectionHolder包装起来放入TransactionSynchronizationManager中ThreadLocal中的资源对象中,在代码执行中则是直接从当前TransactionSynchronizationManager中获取对应的connection执行相应的sql,而DataSourceTransactionManager则是根据相关的逻辑最后调用connection的rollback或者commit操作。
下面代码是对事务传播行为的对应的测试。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import com.test.spring.dao.IUpdateDao;
@Component
public class TestService {
@Autowired
private IUpdateDao updateDao ;
@Autowired
private TestService1 ts1 ;
@Autowired
private TestService2 ts2 ;
@Transactional()
public void doUpdate() {
ts1.doUpdate1();
try {
ts2.doUpdate2();
}catch(Exception e) {
e.printStackTrace();
}
}
@Transactional()
public void doUpdate1() {
updateDao.update("update test set num = num + 100000 where id = 1") ;
}
@Transactional
public void doUpdate2() {
updateDao.update("update test set num = num + 1 where id = 1") ;
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.test.spring.dao.IUpdateDao;
@Component
public class TestService1 {
@Autowired
private IUpdateDao updateDao ;
@Transactional(propagation=Propagation.REQUIRED)
public void doUpdate1() {
updateDao.update("update test set num = num + 100000 where id = 1") ;
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.test.spring.dao.IUpdateDao;
@Component
public class TestService2 {
@Autowired
private IUpdateDao updateDao ;
@Transactional(propagation = Propagation.REQUIRED)
public void doUpdate2() {
updateDao.update("update test set num = num + 1 where id = 1") ;
int t = 1/0 ;
}
}
import java.sql.Connection;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class IUpdateDao {
private DataSource ds ;
public DataSource getDs() {
return ds;
}
public void setDs(DataSource ds) {
this.ds = ds;
}
public boolean update(String sql) {
ConnectionHolder conHolder =
(ConnectionHolder)TransactionSynchronizationManager.getResource(ds) ;
Connection connection = conHolder.getConnection() ;
try {
return connection.prepareStatement(sql).execute() ;
}catch(Exception e) {
e.printStackTrace();
}
return false ;
}
}
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.test.spring.service.TestService;
public class TestTx {
public static void main(String[] args) {
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("classpath:spring-tx.xml") ;
application.refresh();
TestService test = (TestService)application.getBean(TestService.class) ;
test.doUpdate();
}
}
可以看到代码的逻辑其实很简单,就是调用TestService的doUpdate方法,而TestService的doUpdate调用了TestService1的doUpdate1和TestService2的doUpdate2方法。doUpdate1方法是对数据库中表test中id为1的数据加100000,而doUpdate2则是对同一行数据加1。其中doUpdate,doUpdate1和doUpdate2方法都加入了@Transactional注解。表示这三个方法都会进到TransactionInterceptor的对应的处理的方法中。其中doUpdate2方法中由于有1/0必定会抛出异常。
注意:这里没有用TestService的doUpdate1和doUpdate2方法,其主要原因是spring的Aop代理最终使用其原生的bean调用的最终的方法,即为同一个类中方法调用自身类的对应方法是不会进行代理的,即doUpdate如果调用的是自身类的doUpdate1和doUpdate2方法则doUpdate1和doUpdate2是没有经过代理的方法
假设num的初始值为0,下面分几种情况来看一下这个项目的结果:
其结果为0。
分析一下是doUpdate上创建了connection,doUpdate1正确执行,但是其commit操作时不是其创建的connection,即其TransactionStatus不是newTransaction,则其commit没有进行操作,而在doUpdate2中由于其有异常,所以回滚了,但是其也不是newTransaction,则只是将本身标记为rollback,最终在doUpdate中进行commit时发现有rollback标记时则将当前的connection进行rollback,将doUpdate1中的一起回滚了,故而数据没有发生变化。
其结果为100000。
分析一下是doUpdate创建了一个connection为conn1,doUpdate1利用这个conn1更新了数据但是没有commit,而doUpdate2上市Requires_new,则会将当前的conn1挂起重新创建一个connection为conn2,这个conn2也会更新id为1这一行数据,但是由于conn1中的更新还没有提交,则doUpdate2中的conn1的提交,而conn1的提交是在doUpdate2方法执行之后的,因此会造成死锁,但是由于mysql设置了锁最长等待时间,则doUpdate2在等待一段时间会因报错而出来,最终conn1正确提交,结果为100000。
其结果为0
其原因和1差不多,doUpdate2中设置了rollback标志让connect直接回滚。
其结果为100000
其原因是doUpdate创建了connection,在doUpdate1中运行了sql,而doUpdate2由于not_supported挂起了当前的事务,其在IUpdateDao中的TransactionSynchronizationManager无法获取connection,故此无法执行sql,会报错,但是其由于没有事务,也不会进行回滚等操作,doUpdate1的写操作最终会成功。
其结果为100000
原因是doUpdate创建了connection,doUpdate1中正常运行,doUpdate2没有新建connection,但是在执行之前创建了一个savePoint,在其最终回滚时其实是回滚到savePoint,即doUpdate1执行之后的结果,doUpdate正确执行commit后doUpdate1的写成功了。
结果为100000
doUpdate2直接抛出异常,但是没进行操作,但是doUpdate是正确commit,故而是100000
结果为0
doUpdate2和doUpdate1共用一个connection,而且也没有创建savePoint,回滚会直接将所有的回滚。