spring回滚机制源码分析

转自:http://blog.csdn.net/ch_space/article/details/18767875

闲来无事,重新看了下spring事务管理源码,写个笔记。

1、TransactionTemplate

当需要在事务中执行一个DB操作时,执行:

[java] 
view plain
 copy

  1. transactionTemplate.execute(new TransactionCallback<MyDO>{  
  2.     public MyDO doInTransaction(TransactionStatus status){  
  3.         myDao.update();  
  4.         …;  
  5.         myDao.insert();  
  6.     }  
  7. });  

事务中任何操作抛出异常,整个事务就会回滚掉,不用应用代码显式处理。

[java] 
view plain
 copy

  1. public <T> T execute(TransactionCallback<T> action) throws TransactionException {  
  2.         if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {  
  3.             return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);  
  4.         }  
  5.         else {  
  6.             TransactionStatus status = this.transactionManager.getTransaction(this);//这里会根据当前事务状态确定是否开启新事务  
  7.             T result;  
  8.             try {  
  9.                 result = action.doInTransaction(status);  
  10.             }  
  11.             catch (RuntimeException ex) {  
  12.                 // Transactional code threw application exception -> rollback  
  13.                 rollbackOnException(status, ex);  
  14.                 throw ex;  
  15.             }  
  16.             catch (Error err) {  
  17.                 // Transactional code threw error -> rollback  
  18.                 rollbackOnException(status, err);  
  19.                 throw err;  
  20.             }  
  21.             catch (Exception ex) {  
  22.                 // Transactional code threw unexpected exception -> rollback  
  23.                 rollbackOnException(status, ex);  
  24.                 throw new UndeclaredThrowableException(ex, “TransactionCallback threw undeclared checked exception”);  
  25.             }  
  26.             this.transactionManager.commit(status);  
  27.             return result;  
  28.         }  
  29. }  

接下来是如何开启事务的?看transactionManager.getTransaction(this)

2、DataSourceTransactionManager

TransactionTemplate中包含一个dataSourceTransactionManager变量,用来管理事务,一切都是由他关联起来的。

[java] 
view plain
 copy

  1. public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {  
  2.         Object transaction = doGetTransaction();  
  3.         if (isExistingTransaction(transaction)) {//已开启事务,就返回当前事务状态  
  4.             // Existing transaction found -> check propagation behavior to find out how to behave.  
  5.             return handleExistingTransaction(definition, transaction, debugEnabled);  
  6.         }  
  7.         …  
  8.         try {  
  9.             boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);  
  10.             DefaultTransactionStatus status = newTransactionStatus(  
  11.                     definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);  
  12.             doBegin(transaction, definition);//这里就是开启事务啦!  
  13.             prepareSynchronization(status, definition);  
  14.             return status;  
  15.         }  
  16. }  

3、来看下如何doBegin的:

[java] 
view plain
 copy

  1. protected void doBegin(Object transaction, TransactionDefinition definition) {  
  2.     DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;  
  3.     Connection con = null;  
  4.   
  5.     try {  
  6.         if (txObject.getConnectionHolder() == null ||  
  7.                 txObject.getConnectionHolder().isSynchronizedWithTransaction()) {  
  8.             Connection newCon = this.dataSource.getConnection();//看到没?这个就是db连接了~  
  9.             txObject.setConnectionHolder(new ConnectionHolder(newCon), true);//放到连接holder里面  
  10.         }  
  11.           
  12.         // 这里绑定db连接到当前线程,哦,原来是依靠threadlocal实现的!注意,这里的key是Datasource  
  13.         if (txObject.isNewConnectionHolder()) {  
  14.             TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());  
  15.         }  
  16.     }  
  17.     …  
  18. }  

现在事务开启了,链接也放在线程变量里面了,那如何跟dao操作关联起来呢?这里以ibatis为例分析下。

4、从SqlMapClient初始化看起:

[html] 
view plain
 copy

  1. <bean id=“mySqlMapClient” class=“org.springframework.orm.ibatis.SqlMapClientFactoryBean”>  
  2.     <property name=“dataSource” ref=“myDataSource”/>  
  3.     <property name=“configLocations”>  
  4.        <list>  
  5.          <value>classpath:sqlmap/sqlmap-my.xml</value>  
  6.        </list>    
  7.     </property>  
  8. </bean>  

这个dataSource跟事务管理器的dataSource是同一个。看org.springframework.orm.ibatis.SqlMapClientFactoryBean初始化后:

[java] 
view plain
 copy

  1. public void afterPropertiesSet() throws Exception {  
  2.         try {  
  3.             …  
  4.             // Tell the SqlMapClient to use the given DataSource, if any.  
  5.             if (this.dataSource != null) {  
  6.                 TransactionConfig transactionConfig = (TransactionConfig) this.transactionConfigClass.newInstance();  
  7.                 DataSource dataSourceToUse = this.dataSource;  
  8.                 if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) {  
  9.                     dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource);//搞了一个数据源代理  
  10.                 }  
  11.                 transactionConfig.setDataSource(dataSourceToUse);  
  12.                 …  
  13.             }  
  14.         }  
  15. }  

如果没猜错,spring在ibatis从这个代理数据源获取连接时候做了拦截处理,如果当前线程已经开启了事务,就直接用这个事务关联的连接了,看源码:

[java] 
view plain
 copy

  1. public TransactionAwareDataSourceProxy(DataSource targetDataSource) {  
  2.     super(targetDataSource);  
  3. }  

5、继续看怎么取连接的:

[java] 
view plain
 copy

  1. public Connection getConnection() throws SQLException {  
  2.     DataSource ds = getTargetDataSource();  
  3.     Assert.state(ds != null“‘targetDataSource’ is required”);  
  4.     return getTransactionAwareConnectionProxy(ds);//连接也包装成了代理对象,看下这个代理的连接是怎么从datasource搞出来的  
  5. }  

[java] 
view plain
 copy

  1. protected Connection getTransactionAwareConnectionProxy(DataSource targetDataSource) {  
  2.     return (Connection) Proxy.newProxyInstance(  
  3.             ConnectionProxy.class.getClassLoader(),  
  4.             new Class[] {ConnectionProxy.class},  
  5.             new TransactionAwareInvocationHandler(targetDataSource));  
  6. }  
  7.   
  8. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  9.     …  
  10.     Connection actualTarget = this.target;  
  11.     if (actualTarget == null) {  
  12.         actualTarget = DataSourceUtils.doGetConnection(this.targetDataSource);//怎么取的呢?  
  13.     }  
  14.     //真正用这个连接的时候返回原始对象  
  15.     if (method.getName().equals(“getTargetConnection”)) {  
  16.         // Handle getTargetConnection method: return underlying Connection.  
  17.         return actualTarget;  
  18.     }  
  19.     …  
  20. }  

6、看下DataSourceUtils的doGetConnection这个方法:

[java] 
view plain
 copy

  1. public static Connection doGetConnection(DataSource dataSource) throws SQLException {  
  2.     //从当前线程上下文取出连接holder,这个就是开启事务时候放进去的,还记得不?  
  3.     ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);  
  4.     if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {  
  5.         conHolder.requested();  
  6.         if (!conHolder.hasConnection()) {  
  7.             logger.debug(“Fetching resumed JDBC Connection from DataSource”);  
  8.             conHolder.setConnection(dataSource.getConnection());  
  9.         }  
  10.         return conHolder.getConnection();  
  11.     }  
  12.     …  
  13. }  

好了,总结一下:

1)TransactionTemplate.execute执行事务块时,DataSourceTransactionManager开启事务,并把db连接用事务同步器TransactionSynchronizationManager注册到线程变量中中。

2)事务块中实行dao方法时,sqlMapClient从数据源获取连接是通过代理数据源取到了代理连接,在操作db的时候,这个代理连接返回了当前线程上下文中的连接。这样,同一个事务块中的多个dao操作就公用了同一个db连接,实现了事务悄无声息的封装。

    原文作者:Spring Cloud
    原文地址: https://blog.csdn.net/Jamie_Jiang/article/details/78227162
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞