spring回滚机制源码分析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取10G资料包与项目实战视频资料

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

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

1、TransactionTemplate

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

[java]
view plain
copy

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

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

[java]
view plain
copy

  1. public T execute(TransactionCallback 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. try {
  8. boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
  9. DefaultTransactionStatus status = newTransactionStatus(
  10. definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
  11. doBegin(transaction, definition);//这里就是开启事务啦!
  12. prepareSynchronization(status, definition);
  13. return status;
  14. }
  15. }

3、来看下如何doBegin的:

[java]
view plain
copy

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

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

4、从SqlMapClient初始化看起:

[html]
view plain
copy

  1. <**beanid=“mySqlMapClient”class=“org.springframework.orm.ibatis.SqlMapClientFactoryBean”>**
  2. <**propertyname=“dataSource”ref=“myDataSource”/>**
  3. <**propertyname=“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. // Tell the SqlMapClient to use the given DataSource, if any.
  4. if (this.dataSource != null) {
  5. TransactionConfig transactionConfig = (TransactionConfig) this.transactionConfigClass.newInstance();
  6. DataSource dataSourceToUse = this.dataSource;
  7. if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) {
  8. dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource);//搞了一个数据源代理
  9. }
  10. transactionConfig.setDataSource(dataSourceToUse);
  11. }
  12. }
  13. }

如果没猜错,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. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  8. Connection actualTarget = this.target;
  9. if (actualTarget == null) {
  10. actualTarget = DataSourceUtils.doGetConnection(this.targetDataSource);//怎么取的呢?
  11. }
  12. //真正用这个连接的时候返回原始对象
  13. if (method.getName().equals(“getTargetConnection”)) {
  14. // Handle getTargetConnection method: return underlying Connection.
  15. return actualTarget;
  16. }
  17. }

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. }

好了,总结一下:

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

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


来源:http://ddrv.cn/a/88268

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring回滚机制源码分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏