Spring 2.0声明式事务管理让你的事务轻松回滚!

本文介绍Spring框架中事务管理的配置方法,包括XML方式的声明式事务配置,对比不同版本间的配置差异。
 请看下面的接口和它的实现。这个例子的意图是介绍概念,使用 FooBar 这样的名字只是为了让你关注于事务的用法,而不是领域模型。
                <!-- 我们想做成事务性的服务接口 -->
            

package x.y.service;

public interface FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    
void insertFoo(Foo foo);

    
void updateFoo(Foo foo);

}

 

                <!-- 上述接口的一个实现 -->
            

package x.y.service;

public class DefaultFooService implements FooService {

    
public Foo getFoo(String fooName) {
        
throw new UnsupportedOperationException();
    }


    
public Foo getFoo(String fooName, String barName) {
        
throw new UnsupportedOperationException();
    }


    
public void insertFoo(Foo foo) {
        
throw new UnsupportedOperationException();
    }


    
public void updateFoo(Foo foo) {
        
throw new UnsupportedOperationException();
    }

}

(对该例的目的来说,上例中实现类(DefaultFooService)的每个方法在其方法体中抛出 UnsupportedOperationException 的做法是恰当的,我们可以看到,事务被创建出来,响应 UnsupportedOperationException 的抛出,然后回滚。)

我们假定,FooService的前两个方法(getFoo(String)getFoo(String, String))必须执行在只读事务上下文中,其余方法(insertFoo(Foo)updateFoo(Foo))必须执行在读写事务上下文中。

使用XML方式元数据的声明式配置的话,你得这么写(不要想着一次全部理解,所有内容会在后面的章节详细讨论):

 

                <!-- 'context.xml'文件的内容如下: -->
            
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop
="http://www.springframework.org/schema/aop"
       xmlns:tx
="http://www.springframework.org/schema/tx"
       xsi:schemaLocation
="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
>

  
<!-- 这是我们将要配置并使它具有事务性的Service对象 -->
  
  
<bean id="fooService" class="x.y.service.DefaultFooService"/>

  
  
<!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> bean below) -->
  
  
<tx:advice id="txAdvice" transaction-manager="txManager">
    
<!-- the transactional semantics... -->
    
    
<tx:attributes>
      
<!-- all methods starting with 'get' are read-only -->
      
      
<tx:method name="get*" read-only="true"/>
      
<!-- other methods use the default transaction settings (see below) -->
      
      
<tx:method name="*"/>
    
</tx:attributes>
  
</tx:advice>

  
<!-- ensure that the above transactional advice runs for any execution
      of an operation defined by the FooService interface 
-->
      
  
<aop:config>
    
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
    
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
  
</aop:config>

  
<!-- don't forget the DataSource -->
  
  
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
    
<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
    
<property name="username" value="scott"/>
    
<property name="password" value="tiger"/>
  
</bean>

  
  
<!-- similarly, don't forget the (particular) PlatformTransactionManager -->
  
  
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    
<property name="dataSource" ref="dataSource"/>
  
</bean>

  
<!-- other <bean/> definitions here -->
  
</beans>

我们来分析一下上面的配置。我们要把一个服务对象('fooService' bean)做成事务性的。我们想施加的事务语义封装在<tx:advice/>定义中。<tx:advice/>把所有以 'get' 开头的方法看做执行在只读事务上下文中,其余的方法执行在默认语义的事务上下文中”。 其中的 'transaction-manager' 属性被设置为一个指向 PlatformTransactionManager bean的名字(这里指 'txManager'),该bean将实际上实施事务管理。

提示

事实上,如果 PlatformTransactionManager bean的名字是 'transactionManager' 的话,你的事务通知(<tx:advice/>)中的 'transaction-manager' 属性可以忽略。否则你则需要像上例那样明确指定。

配置中最后一段是 <aop:config/> 的定义,它确保由 'txAdvice' bean定义的事务通知在应用中合适的点被执行。首先我们定义了 一个切面,它匹配 FooService 接口定义的所有操作,我们把该切面叫做 'fooServiceOperation'。然后我们用一个通知器(advisor)把这个切面与 'txAdvice' 绑定在一起,表示当 'fooServiceOperation' 执行时,'txAdvice' 定义的通知逻辑将被执行。

一个普遍性的需求是让整个服务层成为事务性的。满足该需求的最好方式是让切面表达式匹配服务层的所有操作方法。例如:

<aop:config>
    
<aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..))"/>
    
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/>
  
</aop:config>

现在,既然我们已经分析了整个配置,你可能会问了,“好吧,但是所有这些配置做了什么?”。

上面的配置将为由 'fooService' 定义的bean创建一个代理对象,这个代理对象被装配了事务通知,所以当它的相应方法被调用时,一个事务将被启动、挂起、被标记为只读,或者其它(根据该方法所配置的事务语义)。

我们来看看下面的例子,测试一下上面的配置。

public final class Boot {

    
public static void main(final String[] args) throws Exception {
        ApplicationContext ctx 
= new ClassPathXmlApplicationContext("context.xml", Boot.class);
        FooService fooService 
= (FooService) ctx.getBean("fooService");
        fooService.insertFoo (
new Foo());
    }

}

运行上面程序的输出结果看起来像这样(注意为了清楚起见,Log4J的消息和从 DefaultFooServiceinsertFoo(..) 方法抛出的 UnsupportedOperationException 异常堆栈信息被省略了)。

 

                <!-- Spring容器开始启动... -->
                
[AspectJInvocationContextExposingAdvisorAutoProxyCreator] 
- Creating implicit proxy
        
for bean 'fooService' with 0 common interceptors and 1 specific interceptors
    
<!-- the DefaultFooService is actually proxied -->
    
[JdkDynamicAopProxy] 
- Creating JDK dynamic proxy for [x.y.service.DefaultFooService]

    
<!-- ... the insertFoo(..) method is now being invoked on the proxy -->
    

[TransactionInterceptor] 
- Getting transaction for x.y.service.FooService.insertFoo
    
<!-- the transactional advice kicks in here... -->
    
[DataSourceTransactionManager] 
- Creating new transaction with name [x.y.service.FooService.insertFoo]
[DataSourceTransactionManager] 
- Acquired Connection
        [org.apache.commons.dbcp.PoolableConnection@a53de4] 
for JDBC transaction

    
<!-- the insertFoo(..) method from DefaultFooService throws an exception... -->
    
[RuleBasedTransactionAttribute] 
- Applying rules to determine whether transaction should
        rollback on java.lang.UnsupportedOperationException
[TransactionInterceptor] 
- Invoking rollback for transaction on x.y.service.FooService.insertFoo
        due to throwable [java.lang.UnsupportedOperationException]

   
<!-- and the transaction is rolled back (by default, RuntimeException instances cause rollback) -->
   
[DataSourceTransactionManager] 
- Rolling back JDBC transaction on Connection
        [org.apache.commons.dbcp.PoolableConnection@a53de4]
[DataSourceTransactionManager] 
- Releasing JDBC Connection after transaction
[DataSourceUtils] 
- Returning JDBC Connection to DataSource

Exception in thread 
"main" java.lang.UnsupportedOperationException
    at x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:
14)
   
<!-- AOP infrastructure stack trace elements removed for clarity -->
   
    at $Proxy0.insertFoo(Unknown Source)
    at Boot.main(Boot.java:
11)

 

 

 

 

附:Spring的事务管理2.0与1.2.x 的区别

Spring以前对一个事务拦截要通过代理实现下面的配置文件是从不同的文件中找来的,不是单独的一个Spring配置文件。

<!-- Transaction manager for a single JDBC DataSource -->
<!-- 声明一个事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- 声明一个抽象Bean,这个Bean是不能实例化的,提供给其它需要AOP事务的Bean用,其它需要AOP事务的只要继承这个Bean就会被AOP接管-->
<bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>

<!-- 继承之前实现的抽象Bean,让这个Bean通过代理工厂生成,交给AOP托管。至于哪些方法被接管在控制Bean中已经配置了-->

<bean id="petStore" parent="baseTransactionProxy">
<property name="target">
<bean class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl">
<property name="accountDao" ref="accountDao"/>
<property name="categoryDao" ref="categoryDao"/>
<property name="productDao" ref="productDao"/>
<property name="itemDao" ref="itemDao"/>
<property name="orderDao" ref="orderDao"/>
</bean>
</property>
<!-- Uncomment the following in order to enable mail sending aspect -->
<!--
<property name="postInterceptors">
<list>
<ref bean="emailAdvisor"/>
</list>
</property>
-->
</bean>

最早发表于 http://www.openj.cn

Spring2.0

下面的配置与上面的配置完全对应

<!--这一个Bean的配置与之前完全一样,没有变化---->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<!--这一处与之前有了变化,在1.2.8版本中,此处的Bean被声明为由一个FactoryBean生成,而此处只是一个普通的Bean,要简单许多,透明性要好很多---->
<bean id="petStore" class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl">
<property name="accountDao" ref="accountDao"/>
<property name="categoryDao" ref="categoryDao"/>
<property name="productDao" ref="productDao"/>
<property name="itemDao" ref="itemDao"/>
<property name="orderDao" ref="orderDao"/>
</bean>

<!---下面的两处配置,才是整个事务AOP的核心,在1.2.8版本中,通过FactoryBean把事务对象(dataSource),与需要进行事务控制的对象PetStoreImpl串起来,对PetStoreImpl有侵入性----->
<!---而在之前的两处配置中,事务对象(dataSource)与,需要进行事务控制的对象PetStoreImpl没有什么关系,它们的关系全部体现在下面的两处配置中----->

<!---pointcut属性定义了哪此点需要去拦截,此处的配置的意思是所有的PetStoreFacade接口中的方法都要拦截,而拦截之后要如何处理则由advice-ref指定的Bean处理----->
<!---配置文件中各个属性的含义参考:http://www.redsaga.com/spring_ref/2.0/html/aop.html#aop-schema ----->

<aop:config>
<aop:advisor pointcut="execution(* *..PetStoreFacade.*(..))" advice-ref="txAdvice"/>
</aop:config>

<!--下面的transaction-manager属性原配置中没有,如果缺少此配置,默认值就是“transactionManager”在此加上,让人看的更明白。-->
<!-- 参考 http://blog.xmnow.cn/doc/cn/spring2.0-reference_final_zh_cn/ch09s05.html --->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*"/>
<tx:method name="update*"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>

<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type=text/javascript> </script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值