转载

Spring 数据库连接(Connection)绑定线程(Thread)的实现

最近在看spring事务的时候在想一个问题:spring中的很多bean都是单例的,是非状态的,而数据库连接是一种有状态的对象,所以spring一定在创建出connection之后在threadlocal中保存了它。今天正好有空,就看了一下源码:

/**

* Bind the given resource for the given key to the current thread.

* @param key the key to bind the value to (usually the resource factory)

* @param value the value to bind (usually the active resource object)

* @throws IllegalStateException if there is already a value bound to the thread

* @see ResourceTransactionManager#getResourceFactory()

*/

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() + "]");

}

if (logger.isTraceEnabled()) {

logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +

Thread.currentThread().getName() + "]");

}

}

代码很简单,以dataSource为key,ConnectionHolder为value存进了一个map里,而这个叫做resources的map是一个Threadlocal变量,存在于当前线程的ThreadlocalMap里。

bindResource是在DataSourceTransactionManager.doBegin()方法中被调用的,来看看这个方法

/**

* This implementation sets the isolation level but ignores the timeout.

*/

@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);

// 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).

//这里设置自动提交由spring控制

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);

}

}

该方法的主要作用都写在注释里了,看一下就好。

本文永久更新链接地址: https://www.linuxidc.com/Linux/2018-03/151261.htm

原文  https://www.linuxidc.com/Linux/2018-03/151261.htm
正文到此结束
Loading...