Spring事务原理源码简述
spring框架的事务其实也是基于jdbc的事务,而一般的jdbc事务代码如下形式
try{
connection.setAutoCommit(false);//turn off autocommit transaction
...数据操作
connection.commit();//commit transaction by self
}
catch (Exception e){
connection.rollback();
e.printStackTrace();
}
如果每次数据库操作都按照这种原生的方式来写,那么代码里面会多出非常多冗余代码,因此spring框架利用自身aop机制,为使用者提供了@transactional注解,实现注解式事务管理
spring的事务其实也是基于上面原生jdbc来做的,因此事务的管理本质上还是对jdbc connection的管理,核心的类和方法如下:
AbstractPlatformTransactionManager 看名字知道这是一个定义基本方法和流程的抽象类,它串联了一个事务的基本操作,包括提交回滚等,可以看作是上面原生jdbc的抽象
DataSourceTransactionManager doBegin() 这是执行事务操作的地方,代码如下,和原生jdbc的connection.setAutoCommit(false)对应,但其中还有一步关键步骤,获取连接
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
//DataSourceTransactionObject是一个包装类,其中有各种事务的属性和连接包装类
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
//获取连接,包装在ConnectionHolder中
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()) {
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);
}
}
这个方法中获取连接是关键,看看spring是如何实现不同请求事务的隔离的,Connection newCon = obtainDataSource().getConnection();这一句获取了一个数据库连接,而下面有这样一句,还带着注释
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
注释翻译过来就是把connection和线程绑定起来,显然就是不同事务隔离的关键,点进去看看:
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
...
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 [" + Thread.currentThread().getName() + "]");
}
}
resources这个变量是关键,他是一个NamedThreadLocal,其实就是ThreadLocal加个名字,正是ThreadLocal实现了connection和线程的对应绑定,要使用时候调用doGetResource(Object actualKey) 方法获取即可