`
m635674608
  • 浏览: 4934219 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

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

 
阅读更多
前几天解释了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

分享到:
评论

相关推荐

    Spring.3.x企业应用开发实战(完整版).part2

    10.3.1 Spring事务传播机制回顾 10.3.2 相互嵌套的服务方法 10.4 多线程的困惑 10.4.1 Spring通过单实例化Bean简化多线程问题 10.4.2 启动独立线程调用事务方法 10.5 联合军种作战的混乱 10.5.1 Spring事务管理器的...

    Spring 2.0 开发参考手册

    6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ Load-time weaving(LTW) 6.9. ...

    Spring-Reference_zh_CN(Spring中文参考手册)

    6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.1.1. @Configurable object的单元测试 6.8.1.2. 多application context情况下的处理 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来...

    Spring中文帮助文档

    6.8.1. 在Spring中使用AspectJ进行domain object的依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7...

    Spring API

    6.8.1. 在Spring中使用AspectJ进行domain object的依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7...

    spring chm文档

    Spring Framework 开发参考手册 Rod Johnson Juergen Hoeller Alef Arendsen Colin Sampaleanu Rob Harrop Thomas Risberg Darren Davison Dmitriy Kopylenko Mark Pollack ...19.2. 使用Spring JMS ...

    Spring3.x企业应用开发实战(完整版) part1

    10.3.1 Spring事务传播机制回顾 10.3.2 相互嵌套的服务方法 10.4 多线程的困惑 10.4.1 Spring通过单实例化Bean简化多线程问题 10.4.2 启动独立线程调用事务方法 10.5 联合军种作战的混乱 10.5.1 Spring事务管理器的...

    Spring高级之注解驱动开发视频教程

    它里面包含了Spring,SpringMVC,SpringData(事务),SrpingTest等等。 其中: Spring本身里面包含了两大核心IOC和AOP。IOC负责降低我们代码间的依赖关系,使我们的项目灵活度更高,可复用性更强。AOP是让方法间...

    springframework.4.3.22.RELEASE

    Spring JavaMailSenderImpl does not show proper message when recipient list is empty [SPR-17540] #22072 Potential resource leak in DataSourceUtils.doGetConnection [SPR-17559] #22091 SpEL, error parsing...

    springframework.5.0.12.RELEASE

    WebClient logs "Only one connection receive subscriber allowed" when response status is an error [SPR-17564] #22096 Potential resource leak in DataSourceUtils.doGetConnection [SPR-17559] #22091 ...

    edb:春天的 jfinal

    jfinalDb 的数据层操作,兼容 spring 和 jfinal 的事务1.1 围绕 DataSource 数据源对象和 jfinal 的配置类 com.jfinal.plugin.activerecord.Config和 spring事务相关的 DataSourceUtils.getConnection、...

    SPRING API 2.0.CHM

    ConnectionHandle ConnectionHolder ConnectionHolder ConnectionProxy ConnectionSpecConnectionFactoryAdapter ConnectorServerFactoryBean ConsoleListener ConstantException Constants ...

    在Spring中用select last_insert_id()时遇到问题

    可用到自己的程序中之后却得不到想要的结果,于是就怀疑到了Spring头上,因为通过基本JDBC测试是没有任何问题的,所以就去跟踪Spring JDBC, 看过源码之后才豁然开朗,原来Spring中如此获得数据库Connection的:...

    spring-framework-reference-4.1.2

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    spring-framework-reference4.1.4

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    c3p0数据库连接

    DataSourceUtils.java c3p0数据库连接

    java工具包

    开发中用到的常用Jar包,以及已经封装好的各种工具类源码,包括DataSourceUtils、MD5Utils和MailUtils,在线支付、json等各种参考资料。

Global site tag (gtag.js) - Google Analytics