传统的J2EE应用中,事务管理是跟EJB绑定在一起来的,那个时候大部分人使用EJB的Local SLSB仅仅是为了使用它的声明式事务管理罢了。随着技术不断向前发展,例如Spring的出现使得很多J2EE的核心理念不得不开始自省,在Spring的冲击之下完善自我变得更合理。于是JTA不在专属于EJB、与之相对的Local Transaction也在更适合的情况下得到重视。
这里简单总结一下Spring的事务管理,先回顾传统的J2EE中事务管理解决方案是全局容器管理事务,事务由应用服务器来协调,服务器登记所有参与事务的资源,在业务方法结束之后根据需要进行提交或者回滚。典型的例子就是EJB CMT,其优点是把繁琐的事务管理功能挪到了EJB部署描述符上,事务管理成了一个无需硬编码的横切面,当然它也有致命的缺点:只有EJB才能使用、必须使用重量级的全局事务管理、对于仅仅使用少量的事务就未免大材小用……
Spring的事务管理涉及初衷:(What we need?)
1、 可编程的事务管理和一致的异常处理机制。
2、 在POJO上面实现的声明式事务、无需要绑定到重量级组件模型上
3、 可插拔的事务策略、以及让资源能够自由加入事务的手段
4、 为分布式容器而准备的JTA支持
5、 针对各种ORM框架集成而提供的数据源组件
下面正式介绍Spring的事务框架组成,主要核心为3个接口:TransactionDefinition、TransactionStatus、PlatformTransactionManager。它们分别的职能如下:
TransactionDefinition
封装所有事务相关属性的设定。针对事务隔离性级别提供的属性有ISOLATION_DEFAULT、ISOLATION_READ_UNCOMMITTED、ISOLATION_READ_COMMITTED、ISOLATION_SERIALIZABLE。针对事务传播提供六个传播属性分别为PROPAGATION_REQUIRED、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY、PROPAGATION_REQUIRES_NEW、PROPAGATION_NOT_SUPPORT、PROPAGATION_NESTED。用过EJB的会发觉这基本和EJB CMT保持一致。
TransactionStatus
它允许事务管理者控制事务的执行,只有3个接口方法:isNewTransaction()、setRollbackOnly()、isRollbackOnly()。其中setRollbackOnly将对当前事务进行回滚操作并将其终止。
PlatformTransactionManager
这是事务管理的策划者,它使用了前面两个接口来完成事务的创建和管理。它提供了getTransaction()、commit()、rollback()3个方法,针对一个给定的TransactionDefinition返回一个TransactionStatus对象,并且对这个状态对象触发提交或者回滚操作。如进行手工编程式的事务管理,基本就是操作这3个接口的实现类的编码在try/catch中间的代码块编写资源存取代码,事务运行结果就是Manager对象的提交或者回滚。
TransactionDefinition definition = new DefaultTransactionDefinition();
//set definition features here
//…
TransactionStatus status = transactionManager.getTransaction(definition);
try{
//do some resource access
//or business logical use these resource
}catch(BusinessException ex){
transactionManager.rollback(status);
throw ex;
}
transactionManager.commit(status);
//return some result
编程式的事务管理要编写比较繁琐的代码,不是常用的方式。我们主要来看更加轻便高效的声明式事务管理(Declarative Transactions)。在Spring容器中声明式意味着我们要告知Spring某个Bean的某个方法需要事务管理,之后Spring会保证此方法被调用时候有事务贯穿其中。这种实现方式需要依赖AOP对方法调用进行拦截。Spring提供两种声明式的方式来创建一个事务管理代理(AOP的核心是动态代理):ProxyFactoryBean和TransactionProxyFactoryBean。看两个例子:
<beans>
<bean id=”myTransactionManager” class=”org.springframework.transaction.jta.JtaTransactionManager”/>
<bean id=”businessObjectTarget” class=”business.BusinessObject”/>
<bean id=”transactionInterceptor” class=”org.springframework.transaction.interceptor.TransactionInterceptor”>
<property name=”transactionManager”>
<ref bean=”myTransactionManager”/>
</property>
<property name=”transactionAttributeSource”>
<value>
business.BusinessObject.method1=PROPAGATION_REQUIRED,-BusinessCheckedException
business.BusinessObject.method2=PROPAGATION_REQUIRED,+BusinessCheckedException
</value>
</property>
</bean>
<!—隐式使用了CGLIB,集成代理对象以子类来代理-->
<bean id=”myBusinessObject” class=”org.springframework.aop.framework.ProxyFactoryBean”>
<property name=”interceptorNames”>
<value>myTransactionInterceptor,businessObjectTarget</value>
</property>
</bean>
<!—显示使用了JDK自带动态代理,必须指定代理接口,与上一个Bean二者选一-->
<bean id=”myBusinessObject” class=”org.springframework.aop.framework.ProxyFactoryBean”>
<property name=”proxyInterfaces”>
<value>business.BusinessInterface</value>
</property>
<property name=”interceptorNames”>
<value>myTransactionInterceptor,businessObjectTarget</value>
</property>
</bean>
</beans>
再看另外一种使用TransactionProxyFactoryBean的方式配置的声明式事务管理:
<beans>
<bean id=”myTransactionManager” class=”org.springframework.transaction.jta.JtaTransactionManager”/>
<bean id=”businessObjectTarget” class=”business.BusinessObject”/>
<bean id=”myBusinessObject” class=”org.springframework.transaction.interceptor.TransactionProxyFactoryBean”>
<property name=”transactionManager”>
<refref=”myTransactionManager”/>
</property>
<property name=”target”>
<ref bean=” businessObjectTarget”/>
</property>
<property name=”transactionAttributes”>
<props>
<prop key=”businessMethod1”>PROGATION_REQUIRE, -BusinessCheckedException</prop>
</props>
</property>
</bean>
</beans>
此配置方法较之前一种比优点是把所有的配置项组织在一起,事务属性在代理类中统一定义。相当于集成了TransactionInterceptor和ProxyFactoryBean。同样代理也可以使用JDK自带或者CGLIB,通过指定proxyTargetClass为true将强制使用CGLIB代理模式。
大体上Spring的事务管理就是这样,注意到所有的事务属性都通过key-value的形式在配置文件中的transactionAttributes或者transactionAttributeSource中指定,但目前更流行的一种方式的通过源码级别的元数据来设置事务属性。这一点我在之前写过的Spring与OpenJPA的集成中已经举过例子。