这篇文章主要探讨怎么用Spring来装配组件及其事务管理。在J2EE工程里连接到一个简单的数据库并不是什么难题,但是如果要综合组装企业类的组件就变得复杂了。一个简单的组件有一个或多个数据库支撑,所以,我们说到整合两个或多个的组件时,我们希望能够维持跨组件的许多数据库的运作的原子性。
J2EE提供了这些组件的容器,可以保证处理的原子性和独立性。在没有J2EE的情况下我们可以用Spring。Spring基于IoC模式(即反转模式),不仅可以配置组件服务,还可以配置相应的方法。为了更好的实现本文的目的,我们使用Hibernate来做相应的后台开发。
装配组件事务 假设在组件库里,我们已经有一个审核组件(audit component),里面有可以被客户端调用的方法。接着,当我们想要构建一个处理订单的体系,我们发现设计需要的OrderListManager组件服务同样需要审核组件服务。OrderListManager创建和管理订单,每一个服务都含有自己的事务属性。当这时调用审核组件,就可以把OrderListManager的处理内容传给它。也许将来新的业务服务(business service)同样需要审核组件,那这时它调用的事务内容已经不一样了。在网络上的结果就是,虽然审核的功能保持不变,但是可以和别的事件功能组合在一起,用这些方法属性来提供不同的运行时的处理参数。
在图1中有两个分开的调用流程。在流程1里,如果客户端含有一个TX内容,OrderListManager 要由一个新的TX开始或者参与其中,取决于客户端在不在TX里以及OrderListManager方法指定的TX属性。这在它调用AuditManager方法的时候仍然适用。
图1. 装配组件事务 EJB体系通过装配者声明正确的事务属性来获得这种适应性。我们不是在探讨是否声明事务管理,因为这会使运行时的事务参数代码发生改变。几乎所有的J2EE工程提供了分布的事务管理来配合提交协议例如X/Open XA specification。
现在的问题是我们能不能不用EJB来获得相同的功能?Spring是其中一种解决方案。来看一下Spring如何处理这样的问题:
用Spring来管理事务 我们将看到的是一个轻量级的事务机制,实际上,它可以管理组件层的事务集成。Spring就是如此。它的优点是我们可以不用捆绑在J2EE的服务例如JNDI数据库。最棒的是如果我们想把这个事务机制与已经存在的J2EE框架组合在一起,没有任何问题,就好像我们找到了杠杆中完美的支撑点一样。
Spring的另一个机制是使用了AOP框架。这个框架使用了一个可以使用AOP的Spring bean factory。在Spring特定的配置文件applicationContext.xml里通过特定的组件层的事件来指定。
<beans><!-- other code goes here... -->
<bean id="orderListManager" class="org.springframework.transaction .interceptor.TransactionProxyFactoryBean"><property name="transactionManager">
<ref local="transactionManager1"/></property><property name="target">
<ref local="orderListManagerTarget"/></property><property name="transactionAttributes">
<props><prop key="getAllOrderList"> PROPAGATION_REQUIRED </prop>
<prop key="getOrderList"> PROPAGATION_REQUIRED </prop> <prop key="createOrderList"> PROPAGATION_REQUIRED </prop>
<prop key="addLineItem"> PROPAGATION_REQUIRED, -com.example.exception.FacadeException </prop> <prop key="getAllLineItems"> PROPAGATION_REQUIRED,readOnly </prop>
<prop key="queryNumberOfLineItems"> PROPAGATION_REQUIRED,readOnly </prop>
</props></property></bean></beans>
一旦我们在服务层指定了事务属性,它们就被一个继承org.springframework.transaction.PlatformTransactionManager 接口的类截获. 这个接口如下:
public interface PlatformTransactionManager{ TransactionStatus getTransaction (TransactionDefinition definition);
void commit(TransactionStatus status);
void rollback(TransactionStatus status);}
Hibernate事务管理 一旦我们决定了使用Hibernate作为ORM工具,我们下一步要做的就是用Hibernate特定的事务管理实例来配置。
<beans><!-- other code goes here... --><bean id="transactionManager1" class="org.springframework.orm.hibernate. HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory1"/>
</property></bean></beans>
我们来看看什么是“装配组件事务”,你也许注意到了那个OrderListManager 特有的TX属性,那个服务层的组件。我们的工程的主要的东西在表2的BDOM里:
图 2. 业务领域对象模型 (BDOM) 为了用实例说明,我们来列出工程里的非功能需求(NFR):
---事务在数据库appfuse1里保存。
---审核时要登入到另一个数据库appfuse2里,出于安全的考虑,数据库有防火墙保护。
---事务组件可以重用。
---所有访问事件必须经过在事务服务层的审核。
出于以上的考虑,我们决定了OrderListManager 服务将委托任何审核记录来调用已有的AuditManager 组件.这产生了表3这样更细致的结构:
图 3. 组件服务结构设计 值得注意的是,由于我们的NFR,我们要映射OrderListManager相关的事物到appfuse1 数据库里去,而审核相关的到appfuse2。这样,任何审核的时候 OrderListManager 组件都会调用AuditManager 组件。我们认为OrderListManager 组件里的所有方法都要执行, 因为我们通过服务来创建次序和具体项目。那么AuditManager 组件里的服务呢? 因为它做的是审核的动作,我们关心的是为系统里所有的事务记录审核情况。这样的需求是,“即使事务事件失败了,我们也要记录登录的审核情况”。AuditManager 组件同样要有自己的事件,因为它同样与自己的数据库有关联。如下所示:
<beans><!—其他代码在这里--><bean id="auditManager" class="org.springframework.transaction. interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref local="transactionManager2"/> </property>
<property name="target"> <ref local="auditManagerTarget"/> </property>
<property name="transactionAttributes">
<props>
<prop key="log">
PROPAGATION_REQUIRES_NEW
</prop>
</props>
</property></bean></beans>
我们现在把注意力放到这两个事务createOrderList 和 addLineItem中来,作为我们的试验。同时注意我们并没有要求最好的设计——你可能注意到了 addLineItem 方法抛出了 FacadeException, 而 createOrderList 没有。在产品设计中,你也许希望每一个方法都处理异常。
public class OrderListManagerImpl
implements OrderListManager{private AuditManager auditManager;public Long createOrderList (OrderList orderList){
Long orderId = orderListDAO. createOrderList(orderList);
auditManager.log(new AuditObject
(ORDER + orderId, CREATE));
return orderId;}public void addLineItem (Long orderId, LineItem lineItem)
throws FacadeException{
Long lineItemId = orderListDAO.
addLineItem(orderId, lineItem);
auditManager.log(new AuditObject
(LINE_ITEM + lineItemId, CREATE));
int numberOfLineItems = orderListDAO.
queryNumberOfLineItems(orderId);
if(numberOfLineItems > 2){
log("Added LineItem " + lineItemId +
" to Order " + orderId + ";
But rolling back *** !");
throw new FacadeException("Make a new
Order for this line item");
}
else{
log("Added LineItem " + lineItemId +
" to Order " + orderId + ".");
}}//其他代码在这里}
要创建一个这个试验的异常,我们已经介绍了其他事务规则规定一个特定的次序不能在同一行里包含两个项目。我们应该注意到createOrderList 和 addLineItem调用了auditManager.log() 方法。你应该也注意到了上面方法中的事务属性。
<bean id="orderListManager"
class="org.springframework.transaction.
interceptor.TransactionProxyFactoryBean">
<pro