Spring事务处理器的设计与实现
Spring事务处理的应用场景
下面,我们以DataSourceTransactionManager和HibernateTransactionManager这两个常用的事务处理器为例,探讨一下在具体的事务处理器中如何实现事务创建、提交和回滚这些底层的事务处理操作。在DataSourceTransationManager和HibernateTransationManager的设计中,如下图所示,它们作为具体的事务管理器实现,和其他事务管理器实现一样,比如JtaTransactionManager,JpaTransactionManager和JdoTransactionManager,继承自AbstractPlatformManager、作为一个基类,AbstractPlatfromManager封装了Spring事务处理中通用的处理部分,比如事务的创建、提交、回滚,事务状态和信息的处理,与线程的绑定等,有了这些通用处理的支持,对于具体的事务管理器而言,它们只需要处理和具体数据源相关的组件设置就可以了,比如在HibernateTransactionManager中,就只需要配置好和Hibnernate事务处理相关的接口以及相关的设置。所以,从这个类设计关系上,我们也可以看到,Spring事务处理的主要过程是分两个部分完成的,通用的事务处理框架是在AbstractPlatformManager中完成,而Spring的事务接口与数据源实现的接口,多半是由具体的事务管理器来完成,它们都是作为AbstractPlatformManager的子类来是使用的。 我们可以看到,在PlatformTransactionManager的设计中,通过PlatformTransactionManager设计了一系列与事务处理息息相关的接口方法,如getTransaction、commit、rollback这些和事务处理相关的统一接口。对于这些接口的实现,很大一部分是由AbstractTransactionManager来完成的,这个类中的doGetTransaction、doCommit等方法和PlatformTransactionManager的方法对应,实现的是事务处理中相对通用的部分。在这个AbstractPlatformManager下,为具体的数据源配置了不同的事务处理器,以处理不同数据源的事务处理,从而形成了一个从抽象到具体的事务处理中间平台设计,使应用通过声明式事务处理,即开即用事务处理服务,隔离那些与特定的数据源相关的具体实现。
DataSourceTransactionManager的实现
我们先看一下DataSourceTransactionManager,在这个事务处理器中,它的实现直接与事务处理的底层实现相关,具体的实现时序如下图所示。在DataSourceTransactionManager中,在事务开始的时候,会调用doBegin方法,首先会得到相对应的Connection,然后可以根据事务设置的需要,对Connection的相关属性进行配置,比如将Connection的autoCommit功能关闭,并对像TimeoutInSeconds这样的事务处理参数进行设置,最后通过TransactionSynchronizationManager来对资源进行绑定。 具体的实现如下代码清单所示。在如下代码清单中,我们可以看到,DataSourceTransactionManager作为AbstractPlatformTransactionManager的子类,在AbstractPlatformTransactionManager中已经为事务实现设计好了一系列的模板方法,比如事务提交、回滚处理等。在DataSourceTransactionManager中,可以看到对模板方法中一些抽象方法的具体实现。例如,由DataSourceTransactionManager的doBegin方法实现负责事务的创建工作。具体来说,如果使用DataSource创建事务,最终通过设置Connection的AutoCommit属性来对事务处理进行配置。在实现过程中,需要把数据库的Connection和当前的线程进行绑定。对于事务的提交和回滚,都是通过直接调用Connection的提交和回滚来完成的,在这个实现过程中,如何取得事务处理场景中的Connection对象,也是一个值得注意的地方。
org.springframework.jdbc.datasource.DataSourceTransactionManager的实现如下:
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
// 注入的DataSource
@Nullable
private DataSource dataSource;
private boolean enforceReadOnly = false;
// 这里是产生Transaction的地方,为Transaction的创建提供服务
// 对数据库而言,事务工作时由Connection来完成的,此处通过ConnectionHolder进行管理,封装到DataSourceTransactionObject对象中,在这个封装过程中增加了许多为事务处理服务的控制数据
@Override
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
// 获取与当前线程绑定的数据库Connection,这个Connection在第一个事务开始的地方与线程绑定
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
// 这里是判断是否已经存在事务的地方,由ConnectionHolder的isTransactionActive属性来控制
@Override
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
// 这里是事务开始的地方
@Override
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. 把当前的数据库Connection和线程绑定
if (txObject.isNewConnectionHolder()) {
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);
}
}
@Override
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
txObject.setConnectionHolder(null);
return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
@Override
protected void doResume(@Nullable Object transaction, Object suspendedResources) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources);
}
// 事务的提交过程
@Override
protected void doCommit(DefaultTransactionStatus status) {
// 取得Connection以后,通过Connection进行提交
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);
}
}
// 事务的回滚过程,使用Connection的rollback方法
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw translateException("JDBC rollback", ex);
}
}
@Override
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Setting JDBC transaction [" + txObject.getConnectionHolder().getConnection() +
"] rollback-only");
}
txObject.setRollbackOnly();
}
@Override
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// Remove the connection holder from the thread, if exposed.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
// Reset connection.
Connection con = txObject.getConnectionHolder().getConnection();
try {
if (txObject.isMustRestoreAutoCommit()) {
con.setAutoCommit(true);
}
DataSourceUtils.resetConnectionAfterTransaction(
con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
if (txObject.isNewConnectionHolder()) {
if (logger.isDebugEnabled()) {
logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
}
DataSourceUtils.releaseConnection(con, this.dataSource);
}
txObject.getConnectionHolder().clear();
}
protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition)
throws SQLException {
if (isEnforceReadOnly() && definition.isReadOnly()) {
try (Statement stmt = con.createStatement()) {
stmt.executeUpdate("SET TRANSACTION READ ONLY");
}
}
}
protected RuntimeException translateException(String task, SQLException ex) {
return new TransactionSystemException(task + " failed", ex);
}
}
上面介绍了使用DataSourceTransactionManager实现事务创建、提交和回滚的过程,基本上与单独使用Connection实现事务处理是一样的,也是通过设置autoCommit属性,调用Connection的commit和rollback方法来完成的。看到这里,大家一定会觉得非常的熟悉。而我们在声明式事务处理中看到的那些事务处理属性,并不在DataSourceTransactionManager中完成,这和我们在前面分析中看到的是一致的。