Spring事务,connection获取,用DataSourceUtils的原理

来源:互联网 发布:js修改button的值 编辑:程序博客网 时间:2024/05/22 17:18
前几天解释了Spring的抽象事务机制。这次讲讲Spring中的DataSource 事务。 
DataSource事务相关的类比较多,我们一步步来拨开其中的密团。 

1 如何获得连接 
看DataSourceUtils代码 

Java代码 
1.protected static Connection doGetConnection(DataSource dataSource, boolean allowSynchronization);   
2.            throws SQLException {   
3.           
4.        ConnectionHolder conHolder = (ConnectionHolder); TransactionSynchronizationManager.getResource(dataSource);;   
5.        if (conHolder != null); {   
6.            conHolder.requested();;   
7.            return conHolder.getConnection();;   
8.        }   
9.  
10.           
11.        Connection con = dataSource.getConnection();;   
12.        if (allowSynchronization && TransactionSynchronizationManager.isSynchronizationActive();); {   
13.                        conHolder = new ConnectionHolder(con);;   
14.            TransactionSynchronizationManager.bindResource(dataSource, conHolder);;   
15.            TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(conHolder, dataSource););;   
16.            conHolder.requested();;   
17.        }   
18.  
19.        return con;   
20.    }  
protected static Connection doGetConnection(DataSource dataSource, boolean allowSynchronization); 
throws SQLException { 

ConnectionHolder conHolder = (ConnectionHolder); TransactionSynchronizationManager.getResource(dataSource);; 
if (conHolder != null); { 
conHolder.requested();; 
return conHolder.getConnection();; 



Connection con = dataSource.getConnection();; 
if (allowSynchronization && TransactionSynchronizationManager.isSynchronizationActive();); { 
conHolder = new ConnectionHolder(con);; 
TransactionSynchronizationManager.bindResource(dataSource, conHolder);; 
TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(conHolder, dataSource););; 
conHolder.requested();; 


return con; 

原来连接是从TransactionSynchronizationManager中获取,如果TransactionSynchronizationManager中已经有了,那么拿过来然后调用conHolder.requested()。否则从原始的DataSource这创建一个连接,放到一个ConnectionHolder,然后再调用TransactionSynchronizationManager.bindResource绑定。 
好,我们又遇到两个新的类TransactionSynchronizationManager和ConnectionHolder和。继续跟踪 


2 TransactionSynchronizationManager 
看其中的一些代码 

Java代码 
1.private static ThreadLocal resources = new ThreadLocal();;   
2.public static Object getResource(Object key); {   
3.        Map map = (Map); resources.get();;   
4.        if (map == null); {   
5.            return null;   
6.        }   
7.        Object value = map.get(key);;   
8.                return value;   
9.    }   
10.public static void bindResource(Object key, Object value); throws IllegalStateException {   
11.        Map map = (Map); resources.get();;   
12.                if (map == null); {   
13.            map = new HashMap();;   
14.            resources.set(map);;   
15.        }   
16.        map.put(key, value);;   
17.            }  
private static ThreadLocal resources = new ThreadLocal();; 
public static Object getResource(Object key); { 
Map map = (Map); resources.get();; 
if (map == null); { 
return null; 

Object value = map.get(key);; 
return value; 

public static void bindResource(Object key, Object value); throws IllegalStateException { 
Map map = (Map); resources.get();; 
if (map == null); { 
map = new HashMap();; 
resources.set(map);; 

map.put(key, value);; 
}原来TransactionSynchronizationManager内部建立了一个ThreadLocal的resources,这个resources又是和一个map联系在一起的,这个map在某个线程第一次调用bindResource时生成。 
联系前面的DataSourceUtils代码,我们可以总结出来。 
某个线程使用DataSourceUtils,当第一次要求创建连接将在TransactionSynchronizationManager中创建出一个ThreadLocal的map。然后以DataSource作为键,ConnectionHolder为值放到map中。等这个线程下一次再请求的这个DataSource的时候,就从这个map中获取对应的ConnectionHolder。用map是为了解决同一个线程上多个DataSource。 
然后我们来看看ConnectionHolder又是什么? 



3 对连接进行引用计数 
看ConnectionHolder代码,这个类很简单,看不出个所以然,只好再去看父类代码ResourceHolderSupport,我们感兴趣的是这两个方法 

Java代码 
1.public void requested(); {   
2.        this.referenceCount++;   
3.    }   
4.  
5.    public void released(); {   
6.        this.referenceCount--;   
7.    }  
public void requested(); { 
this.referenceCount++; 


public void released(); { 
this.referenceCount--; 

看得出这是一个引用计数的技巧。原来Spring中对Connection是竟量使用已创建的对象,而不是每次都创建一个新对象。这就是DataSourceUtils中 
Java代码 
1.if (conHolder != null); {   
2.            conHolder.requested();;   
3.            return conHolder.getConnection();;   
4.        }  
if (conHolder != null); { 
conHolder.requested();; 
return conHolder.getConnection();; 
}的原因 


4 释放连接 
完成事物后DataSourceTransactionManager有这样的代码 

Java代码 
1.protected void doCleanupAfterCompletion(Object transaction); {   
2.        DataSourceTransactionObject txObject = (DataSourceTransactionObject); transaction;   
3.  
4.        // Remove the connection holder from the thread.   
5.        TransactionSynchronizationManager.unbindResource(this.dataSource);;   
6.        txObject.getConnectionHolder();.clear();;   
7.  
8.        //...       DataSourceUtils.closeConnectionIfNecessary(con, this.dataSource);;   
9.    }  
protected void doCleanupAfterCompletion(Object transaction); { 
DataSourceTransactionObject txObject = (DataSourceTransactionObject); transaction; 

// Remove the connection holder from the thread. 
TransactionSynchronizationManager.unbindResource(this.dataSource);; 
txObject.getConnectionHolder();.clear();; 

//... DataSourceUtils.closeConnectionIfNecessary(con, this.dataSource);; 

DataSourceUtils 

Java代码 
1.protected static void doCloseConnectionIfNecessary(Connection con, DataSource dataSource); throws SQLException {   
2.        if (con == null); {   
3.            return;   
4.        }   
5.  
6.        ConnectionHolder conHolder = (ConnectionHolder); TransactionSynchronizationManager.getResource(dataSource);;   
7.        if (conHolder != null && con == conHolder.getConnection();); {   
8.            // It's the transactional Connection: Don't close it.   
9.            conHolder.released();;   
10.            return;   
11.        }   
12.           
13.        // Leave the Connection open only if the DataSource is our   
14.        // special data source, and it wants the Connection left open.   
15.        if (!(dataSource instanceof SmartDataSource); || ((SmartDataSource); dataSource);.shouldClose(con);); {   
16.            logger.debug("Closing JDBC connection");;   
17.            con.close();;   
18.        }   
19.    }  
protected static void doCloseConnectionIfNecessary(Connection con, DataSource dataSource); throws SQLException { 
if (con == null); { 
return; 


ConnectionHolder conHolder = (ConnectionHolder); TransactionSynchronizationManager.getResource(dataSource);; 
if (conHolder != null && con == conHolder.getConnection();); { 
// It's the transactional Connection: Don't close it. 
conHolder.released();; 
return; 


// Leave the Connection open only if the DataSource is our 
// special data source, and it wants the Connection left open. 
if (!(dataSource instanceof SmartDataSource); || ((SmartDataSource); dataSource);.shouldClose(con);); { 
logger.debug("Closing JDBC connection");; 
con.close();; 


恍然大悟。如果事物完成,那么就 
TransactionSynchronizationManager.unbindResource(this.dataSource);将当前的ConnectionHolder 
从TransactionSynchronizationManager上脱离,然后doCloseConnectionIfNecessary。最后会把连接关闭掉。 

5 两个辅助类JdbcTemplate和TransactionAwareDataSourceProxy 
JdbcTemplate中的execute方法的第一句和最后一句 

Java代码 
1.public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action);   
2.            throws DataAccessException {   
3.  
4.        Connection con = DataSourceUtils.getConnection(getDataSource(););;   
5.        //其他代码   
6.    DataSourceUtils.closeConnectionIfNecessary(con, getDataSource(););;   
7.        }   
8.    }  
public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action); 
throws DataAccessException { 

Connection con = DataSourceUtils.getConnection(getDataSource(););; 
//其他代码 
DataSourceUtils.closeConnectionIfNecessary(con, getDataSource(););; 


作用不言自明了吧 

从TransactionAwareDataSourceProxy中获取的连接是这个样子的 

Java代码 
1.public Connection getConnection(); throws SQLException {   
2.        Connection con = DataSourceUtils.doGetConnection(getTargetDataSource();, true);;   
3.        return getTransactionAwareConnectionProxy(con, getTargetDataSource(););;   
4.    }  
public Connection getConnection(); throws SQLException { 
Connection con = DataSourceUtils.doGetConnection(getTargetDataSource();, true);; 
return getTransactionAwareConnectionProxy(con, getTargetDataSource(););; 

万变不离其宗,不过我们还是看看getTransactionAwareConnectionProxy 

Java代码 
1.protected Connection getTransactionAwareConnectionProxy(Connection target, DataSource dataSource); {  
2.        return (Connection); Proxy.newProxyInstance(   
3.                ConnectionProxy.class.getClassLoader();,   
4.                new Class[] {ConnectionProxy.class},   
5.                new TransactionAwareInvocationHandler(target, dataSource););;   
6.    }  
protected Connection getTransactionAwareConnectionProxy(Connection target, DataSource dataSource); { 
return (Connection); Proxy.newProxyInstance( 
ConnectionProxy.class.getClassLoader();, 
new Class[] {ConnectionProxy.class}, 
new TransactionAwareInvocationHandler(target, dataSource););; 

原来返回的是jdk的动态代理。继续看TransactionAwareInvocationHandler 

Java代码 
1.public Object invoke(Object proxy, Method method, Object[] args); throws Throwable {   
2.        //...           if (method.getName();.equals(CONNECTION_CLOSE_METHOD_NAME);); {   
3.                if (this.dataSource != null); {   
4.                    DataSourceUtils.doCloseConnectionIfNecessary(this.target, this.dataSource);;   
5.                }   
6.                return null;   
7.            }   
8.  
9.                    }  
public Object invoke(Object proxy, Method method, Object[] args); throws Throwable { 
//... if (method.getName();.equals(CONNECTION_CLOSE_METHOD_NAME);); { 
if (this.dataSource != null); { 
DataSourceUtils.doCloseConnectionIfNecessary(this.target, this.dataSource);; 

return null; 



TransactionAwareDataSourceProxy会先从DataSourceUtils获取连接。然后将这个连接用jdk的动态代理包一下返回。外部代码如果调用的这个冒牌的Connection,就会先调用TransactionAwareInvocationHandler的invoke,在这个invoke 中,完成原来调用DataSourceUtils的功能。 

总结上面的流程 
Spring 对DataSource进行事务管理的关键在于ConnectionHolder和TransactionSynchronizationManager。 
  0.先从TransactionSynchronizationManager中尝试获取连接 
  1.如果前一步失败则在每个线程上,对每个DataSouce只创建一个Connection 
   2.这个Connection用ConnectionHolder包装起来,由TransactionSynchronizationManager管理 
  3.再次请求同一个连接的时候,从TransactionSynchronizationManager返回已经创建的ConnectionHolder,然后调用ConnectionHolder的request将引用计数+1 
  4.释放连接时要调用ConnectionHolder的released,将引用计数-1 
  5.当事物完成后,将ConnectionHolder从TransactionSynchronizationManager中解除。当谁都不用,这个连接被close 

以上所有都是可以调用DataSourceUtils化简代码,而JdbcTemplate又是调用DataSourceUtils的。所以在Spring文档中要求尽量首先使用JdbcTemplate,其次是用DataSourceUtils来获取和释放连接。至于TransactionAwareDataSourceProxy,那是下策的下策。不过可以将Spring事务管理和遗留代码无缝集成。 

所以如某位朋友说要使用Spring的事务管理,但是又不想用JdbcTemplate,那么可以考虑TransactionAwareDataSourceProxy。这个类是原来DataSource的代理。 
其次,想使用Spring事物,又不想对Spring进行依赖是不可能的。与其试图自己模拟DataSourceUtils,不如直接使用现成的

http://yangleiol.iteye.com/blog/731694

0 0
原创粉丝点击