闲来无事,重新看了下spring事务管理源码,写个笔记。
1、TransactionTemplate
当需要在事务中执行一个DB操作时,执行:
-
transactionTemplate.execute(new TransactionCallback<MyDO>{
-
public MyDO doInTransaction(TransactionStatus status){
-
myDao.update();
-
...;
-
myDao.insert();
-
}
-
});
事务中任何操作抛出异常,整个事务就会回滚掉,不用应用代码显式处理。
-
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
-
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
-
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
-
}
-
else {
-
TransactionStatus status = this.transactionManager.getTransaction(this);//这里会根据当前事务状态确定是否开启新事务
-
T result;
-
try {
-
result = action.doInTransaction(status);
-
}
-
catch (RuntimeException ex) {
-
// Transactional code threw application exception -> rollback
-
rollbackOnException(status, ex);
-
throw ex;
-
}
-
catch (Error err) {
-
// Transactional code threw error -> rollback
-
rollbackOnException(status, err);
-
throw err;
-
}
-
catch (Exception ex) {
-
// Transactional code threw unexpected exception -> rollback
-
rollbackOnException(status, ex);
-
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
-
}
-
this.transactionManager.commit(status);
-
return result;
-
}
-
}
接下来是如何开启事务的?看transactionManager.getTransaction(this)
2、DataSourceTransactionManager
TransactionTemplate中包含一个dataSourceTransactionManager变量,用来管理事务,一切都是由他关联起来的。
-
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
-
Object transaction = doGetTransaction();
-
if (isExistingTransaction(transaction)) {//已开启事务,就返回当前事务状态
-
// Existing transaction found -> check propagation behavior to find out how to behave.
-
return handleExistingTransaction(definition, transaction, debugEnabled);
-
}
-
...
-
try {
-
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
-
DefaultTransactionStatus status = newTransactionStatus(
-
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
-
doBegin(transaction, definition);//这里就是开启事务啦!
-
prepareSynchronization(status, definition);
-
return status;
-
}
-
}
3、来看下如何doBegin的:
-
protected void doBegin(Object transaction, TransactionDefinition definition) {
-
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
-
Connection con = null;
-
try {
-
if (txObject.getConnectionHolder() == null ||
-
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
-
Connection newCon = this.dataSource.getConnection();//看到没?这个就是db连接了~
-
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);//放到连接holder里面
-
}
-
// 这里绑定db连接到当前线程,哦,原来是依靠threadlocal实现的!注意,这里的key是Datasource
-
if (txObject.isNewConnectionHolder()) {
-
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
-
}
-
}
-
...
-
}
现在事务开启了,链接也放在线程变量里面了,那如何跟dao操作关联起来呢?这里以ibatis为例分析下。
4、从SqlMapClient初始化看起:
-
<bean id="mySqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
-
<property name="dataSource" ref="myDataSource"/>
-
<property name="configLocations">
-
<list>
-
<value>classpath:sqlmap/sqlmap-my.xml</value>
-
</list>
-
</property>
-
</bean>
这个dataSource跟事务管理器的dataSource是同一个。看org.springframework.orm.ibatis.SqlMapClientFactoryBean初始化后:
-
public void afterPropertiesSet() throws Exception {
-
try {
-
...
-
// Tell the SqlMapClient to use the given DataSource, if any.
-
if (this.dataSource != null) {
-
TransactionConfig transactionConfig = (TransactionConfig) this.transactionConfigClass.newInstance();
-
DataSource dataSourceToUse = this.dataSource;
-
if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) {
-
dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource);//搞了一个数据源代理
-
}
-
transactionConfig.setDataSource(dataSourceToUse);
-
...
-
}
-
}
-
}
如果没猜错,spring在ibatis从这个代理数据源获取连接时候做了拦截处理,如果当前线程已经开启了事务,就直接用这个事务关联的连接了,看源码:
-
public TransactionAwareDataSourceProxy(DataSource targetDataSource) {
-
super(targetDataSource);
-
}
5、继续看怎么取连接的:
-
public Connection getConnection() throws SQLException {
-
DataSource ds = getTargetDataSource();
-
Assert.state(ds != null, "'targetDataSource' is required");
-
return getTransactionAwareConnectionProxy(ds);//连接也包装成了代理对象,看下这个代理的连接是怎么从datasource搞出来的
-
}
-
protected Connection getTransactionAwareConnectionProxy(DataSource targetDataSource) {
-
return (Connection) Proxy.newProxyInstance(
-
ConnectionProxy.class.getClassLoader(),
-
new Class[] {ConnectionProxy.class},
-
new TransactionAwareInvocationHandler(targetDataSource));
-
}
-
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-
...
-
Connection actualTarget = this.target;
-
if (actualTarget == null) {
-
actualTarget = DataSourceUtils.doGetConnection(this.targetDataSource);//怎么取的呢?
-
}
-
//真正用这个连接的时候返回原始对象
-
if (method.getName().equals("getTargetConnection")) {
-
// Handle getTargetConnection method: return underlying Connection.
-
return actualTarget;
-
}
-
...
-
}
6、看下DataSourceUtils的doGetConnection这个方法:
-
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
-
//从当前线程上下文取出连接holder,这个就是开启事务时候放进去的,还记得不?
-
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
-
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
-
conHolder.requested();
-
if (!conHolder.hasConnection()) {
-
logger.debug("Fetching resumed JDBC Connection from DataSource");
-
conHolder.setConnection(dataSource.getConnection());
-
}
-
return conHolder.getConnection();
-
}
-
...
-
}
好了,总结一下:
1)TransactionTemplate.execute执行事务块时,DataSourceTransactionManager开启事务,并把db连接用事务同步器TransactionSynchronizationManager注册到线程变量中中。
2)事务块中实行dao方法时,sqlMapClient从数据源获取连接是通过代理数据源取到了代理连接,在操作db的时候,这个代理连接返回了当前线程上下文中的连接。这样,同一个事务块中的多个dao操作就公用了同一个db连接,实现了事务悄无声息的封装。