您的位置:首页 > 编程语言 > Java开发

Spring事务管理高级应用难点剖析

2011-12-27 13:16 351 查看
Spring事务管理高级应用难点剖析(1)

Spring最成功,最吸引人的地方莫过于轻量级的声明式事务管理,仅此一点,它就宣告了重量级EJB容器的覆灭。Spring声明式事务管理将开发者从繁复的事务管理代码中解脱出来,专注于业务逻辑的开发上,这是一件可以被拿来顶礼膜拜的事情。

但是,世界并未从此消停,开发人员需要面对的是层出不穷的应用场景,这些场景往往逾越了普通Spring技术书籍的理想界定。因此,随着应用开发的深入,在使用经过Spring层层封装的声明式事务时,开发人员越来越觉得自己坠入了迷雾,陷入了沼泽,体会不到外界所宣称的那种畅快淋漓。本系列文章的目标旨在整理并剖析实际应用中种种让我们迷茫的场景,让阳光照进云遮雾障的山头。

很少有使用Spring但不使用Spring事务管理器的应用,因此常常有人会问:是否用了Spring,就一定要用Spring事务管理器,否则就无法进行数据的持久化操作呢?事务管理器和DAO是什么关系呢?

也许是DAO和事务管理如影随行的缘故吧,这个看似简单的问题实实在在地存在着,从初学者心中涌出,萦绕在开发老手的脑际。答案当然是否定的!我们都知道:Spring事务管理是保证数据操作的事务性(即原子性、一致性、隔离性、持久性,也即所谓的ACID),脱离了事务性,DAO照样可以顺利地进行数据的操作。

在默认情况下,dataSource数据源的autoCommit被设置为true――这也意谓着所有通过Jdbc执行的语句马上提交,没有事务。如果将dataSource的defaultAutoCommit设置为false,新增及更改数据的操作都没有提交到数据库。对于强调读速度的应用,数据库本身可能就不支持事务,如使用MyISAM引擎的MySQL数据库。这时,无须在Spring应用中配置事务管理器,因为即使配置了,也是没有实际用处的。也就是说使用MySql数据库时jdbc设置autoCommit是没有实际用处的。对于Hibernate来说,情况就有点复杂了。因为Hibernate的事务管理拥有其自身的意义,它和Hibernate一级缓存有密切的关系:当我们调用Session的save、update等方法时,Hibernate并不直接向数据库发送SQL语句,而是在提交事务(commit)或flush一级缓存时才真正向数据库发送SQL。所以,即使底层数据库不支持事务,Hibernate的事务管理也是有一定好处的,不会对数据操作的效率造成负面影响。所以,如果是使用Hibernate数据访问技术,没有理由不配置HibernateTransactionManager事务管理器。

Spring事务管理高级应用难点剖析(2)

应用分层的迷惑

Web、Service及DAO三层划分就像西方国家的立法、行政、司法三权分立一样被奉为金科玉律,甚至有开发人员认为如果要使用Spring事务管理就一定先要进行三层的划分。这个看似荒唐的论调在开发人员中颇有市场。更有甚者,认为每层必须先定义一个接口,然后再定义一个实现类。其结果是:一个很简单的功能,也至少需要3个接口,3个类,再加上视图层的JSP和JS等,打牌都可以转上两桌了,这种误解贻害不浅。

对将“面向接口编程”奉为圭臬,认为放之四海而皆准的论调,笔者深不以为然。是的,“面向接口编程”是MartinFowler,RodJohnson这些大师提倡的行事原则。如果拿这条原则去开发架构,开发产品,怎么强调都不为过。但是,对于我们一般的开发人员来说,做的最多的是普通工程项目,往往最多的只是一些对数据库增、删、查、改的功能。此时,“面向接口编程”除了带来更多的类文件外,看不到更多其它的好处。

Spring框架提供的所有附加的好处(AOP、注解增强、注解MVC等)唯一的前提就是让POJO的类变成一个受Spring容器管理的Bean,除此以外没有其它任何的要求。

Spring事务管理高级应用难点剖析(3)

事务方法嵌套调用的迷茫

Spring事务一个被讹传很广说法是:一个事务方法不应该调用另一个事务方法,否则将产生两个事务。结果造成开发人员在设计事务方法时束手束脚,生怕一不小心就踩到地雷。其实这种是不认识Spring事务传播机制而造成的误解,Spring对事务控制的支持统一在TransactionDefinition类中描述,该类有以下几个重要的接口方法:

◆intgetPropagationBehavior():事务的传播行为;

◆intgetIsolationLevel():事务的隔离级别;

◆intgetTimeout():事务的过期时间;

◆booleanisReadOnly():事务的读写特性。

很明显,除了事务的传播行为外,事务的其它特性Spring是借助底层资源的功能来完成的,Spring无非只充当个代理的角色。但是事务的传播行为却是Spring凭借自身的框架提供的功能,是Spring提供给开发者最珍贵的礼物,讹传的说法玷污了Spring事务框架最美丽的光环。所谓事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。Spring支持7种事务传播行为:

◆PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。

◆PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。

◆PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。

◆PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。

◆PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

◆PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。

◆PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

Spring默认的事务传播行为是PROPAGATION_REQUIRED,它适合于绝大多数的情况。假设ServiveX#methodX()都工作在事务环境下(即都被Spring事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这3个服务类的3个方法通过Spring的事务传播机制都工作在同一个事务中。

Spring事务管理高级应用难点剖析(4)

多线程的困惑

由于Spring事务管理器是通过线程相关的ThreadLocal来保存数据访问基础设施,再结合IOC和AOP实现高级声明式事务的功能,所以Spring的事务天然地和线程有着千丝万缕的联系。

我们知道Web容器本身就是多线程的,Web容器为一个Http请求创建一个独立的线程,所以由此请求所牵涉到的Spring容器中的Bean也是运行于多线程的环境下。在绝大多数情况下,Spring的Bean都是单实例的(singleton),单实例Bean的最大的好处是线程无关性,不存在多线程并发访问的问题,也即是线程安全的。一个类能够以单实例的方式运行的前提是“无状态”:即一个类不能拥有状态化的成员变量。我们知道,在传统的编程中,DAO必须执有一个Connection,而Connection即是状态化的对象。所以传统的DAO不能做成单实例的,每次要用时都必须new一个新的实例。传统的Service由于将有状态的DAO作为成员变量,所以传统的Service本身也是有状态的。

但是在Spring中,DAO和Service都以单实例的方式存在。Spring是通过ThreadLocal将有状态的变量(如Connection等)本地线程化,达到另一个层面上的“线程无关”,从而实现线程安全。Spring不遗余力地将状态化的对象无状态化,就是要达到单实例化Bean的目的。由于Spring已经通过ThreadLocal的设施将Bean无状态化,所以Spring中单实例Bean对线程安全问题拥有了一种天生的免疫能力。不但单实例的Service可以成功运行于多线程环境中,Service本身还可以自由地启动独立线程以执行其它的Service。

小结

Spring声明式事务是Spring最核心,最常用的功能。由于Spring通过IOC和AOP的功能非常透明地实现了声明式事务的功能,一般的开发者基本上无须了解Spring声明式事务的内部细节,仅需要懂得如何配置就可以了。

但是在实际应用开发过程中,Spring的这种透明的高阶封装在带来便利的同时,也给我们带来了迷惑。就像通过流言传播的消息,最终听众已经不清楚事情的真相了,而这对于应用开发来说是很危险的。本系列文章通过剖析实际应用中给开发者造成迷惑的各种难点,通过分析Spring事务管理的内部运作机制将真相还原出来。在本文中,我们通过剖析了解到以下的真相:

◆在没有事务管理的情况下,DAO照样可以顺利进行数据操作;

◆将应用分成Web,Service及DAO层只是一种参考的开发模式,并非是事务管理工作的前提条件;

◆Spring通过事务传播机制可以很好地应对事务方法嵌套调用的情况,开发者无须为了事务管理而刻意改变服务方法的设计;

◆由于单实例的对象不存在线程安全问题,所以进行事务管理增强的Bean可以很好地工作在多线程环境下。

◆混合使用多种数据访问技术(如SpringJDBC+Hibernate)的事务管理问题;

◆在通过Bean的方法通过SpringAOP增强存在哪些特殊的情况。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: