Spring 声明式事物配置文件解析

来源:互联网 发布:sql向表中更改字段 编辑:程序博客网 时间:2024/05/21 09:26

11.5.5 <tx:advice/>settings


这节总结了可以使用<tx:advice>标签指定的不同事物设置。默认的<tx:advice/>设置是:

  • 传播设置是必须的
  • 隔离级别是默认的
  • 事物是读/写的
  • 事物超时是底层事物系统的默认超时,或者超时不支持即为空
  • 任何运行期异常触发回滚,并且任何检查型异常不触发

你可以变更这些默认的设置;<tx:method/>标签的不同属性嵌入在<tx:advice/>之内,并且<tx:attributes/>标签总结如下:

Table 11.1. <tx:method/> settings

属性           是否必须                     默认                                描述

name                是                                                            事物属性关联的方法名。通配符*可用于关联大量方法的相同事物属性设置;例如,get*,handle*,on*Event,等等

propagation    否                          REQUIRED             事物传播行为

isolation           否                          DEFAULT                事物隔离级别

timeout           否                               -1                           事物超时时间(以秒为单位)

read-only        否                                                             这个事物是否只读?

rollback-for    否                                                              异常触发的回滚:逗号隔开。例如,com.foo.MyBusinessException,ServletException.

no-rollback-for 否                                                           异常不触发回滚:逗号隔开。例如,com.foo.MyBusinessException,ServletException.




11.5.6  使用@Transactional


除了使用基于XML的声明方式配置事物外,你可以使用基于注解的方式。在Java源代码中声明式语义使得声明与受影响的代码更近。不当的结合没有太多的危险,因为用于事物的代码将总是以任何方式部署。


下面的例子是使用@Transaction注解的例子。考虑下面的类定义:


// the service class that we want to make transactional@Transactionalpublic class DefaultFooService implements FooService {    Foo getFoo(String fooName);    Foo getFoo(String fooName, String barName);    void insertFoo(Foo foo);    void updateFoo(Foo foo);}

当上述的POJO被定义为Spring IoC容器中的一个bean时,可以通过在XML配置中仅仅添加一行使得这个bean是事物性的。


<!-- from the file 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.xsd        http://www.springframework.org/schema/tx        http://www.springframework.org/schema/tx/spring-tx.xsd        http://www.springframework.org/schema/aop        http://www.springframework.org/schema/aop/spring-aop.xsd">    <!-- this is the service object that we want to make transactional -->    <bean id="fooService" class="x.y.service.DefaultFooService"/>    <!-- enable the configuration of transactional behavior based on annotations -->    <tx:annotation-driven transaction-manager="txManager"/><!-- a PlatformTransactionManager is still required -->    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <!-- (this dependency is defined somewhere else) -->        <property name="dataSource" ref="dataSource"/>    </bean>    <!-- other <bean/> definitions here --></beans>

注意:如果你想要封装的PlatformTransactionManager的bean名有名字transactionManager,那么你可以标签<tx:annotation-driven/>transaction-manager元素。如果你想依赖注入的 PlatformTransactionManager bean有其他名字,那就必须直接使用transaction-manager元素,如同前面的例子一样。


@EnableTransactionManagement注解提供了基于Java配置相同的注解。仅仅是将其添加到@Configuration类。


@Transactional注解可以放在一个接口定义前,一个接口的方法前,一个定义类,或者类的public方法前。然而,仅仅通过@Transactional注解不能激发事物行为。@Transactional仅仅是可供运行底层(需要@Transactional提醒的)消耗的元数据,并且可以使用元数据配置有事物行为的合适的bean。在前面的例子中,<tx:annotation-driven/>元素可在事物行为上转换。

注意:Spring推荐使用@Transactional仅注解具体的类(和具体类的方法),而不是注解接口。虽然可以使用@Transactional直接注解在接口上或接口方法上,但是这仅在你想基于接口代理的时候使用才有效。实际上Java注解不从接口继承,意味着如果你想使用基于类的代理(proxy-target-class="true")或者基本封装方面(mode="aspectj"),那么事物设置不会被代理和封装底层识别,并且对象不封装在事物代理中,这样明显很糟糕。


注意:在代理模式中(默认的),仅仅通过代理的外部方法调用被拦截。这意味着自调用,实际上,目标对象内的一个方法调用了目标对象的另一个方法,将不会在运行期导致一个确切的事物,即使那个方法被@Transaction标示。


如果希望自调用封装了事物,考虑AspectJ模式的使用。在这种情况下,首先将不会有一个代理。而是将目标对象编织封装(即修改其字节码),这样将@Transaction转为任何种类方法的运行期行为。


table 11.2 注解驱动的事物设置


XML 属性                                     注解属性                   默认                                 描述

transaction-manager              N/A                             transactionManager     transaction manager 使用的名字。如果事物管理的名字不是transactionManager 则是必须的,

                                                                                                                                  如同上面的例子

mode                                          mode                         proxy                               默认模式“proxy”处理注解的要被代理的beans,使用Spring AOP框架(遵循代理语义,如同上面讨论的,应用于仅通过代理的方法调用)。可选的模式“aspectj”使用Spring AspectJ事物方面编织受影响的类,并修改目标类字节码应用于任何种类的方法调用上。AspectJ编织需要类路径的spring-aspectj.jar,也需要加载时编织(或者编译时编织)


proxy-target-class                    proxyTargetClass   false                                  仅仅用于代理模式。为@Transaction注解的类控制创建哪种类型的事物代理。如果proxy-target-class属性设置为true,那么创建基于类的代理。如果这个属性是false,或者忽略了这个属性,那么创建基于代理的标准JDK接口。


order                                           order                       Order.LOWEST_PRECEDENCE    定义应用于@Transaction注解beans的事物建议顺序。没有指定顺序意味着AOP子系统决定了advice的顺序。


注意:proxy-target-class 决定了为@Transactional注解的类创建哪种类型的事物代理。如果此元素设置为true,创建基于类的代理。如果这个属性设置为false,或者如果忽略这个属性,创建基于接口的标准JDK代理


注意:@EnableTransactionManagement<tx:annotation-driven/>仅寻找在相同应用程序上定义的@Transactional的bean。意味着,如果你为了DispatchServlet将注解驱动放置于一个WebApplicationContext中,仅在你的控制器中检核@Transaction的bean,而不是在你的Service bean中。


当评估一个方法的事物设置时,最低层的衍生位置优先。在下面的例子中,使用read-only事物在类级别注解DefaultFooService,但是了同一个类的updateFoo(Foo foo)方法上的@Transactional注解覆盖了类级别的注解。



@Transactional(readOnly = true)public class DefaultFooService implements FooService {    public Foo getFoo(String fooName) {        // do something    }    // these settings have precedence for this method    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)    public void updateFoo(Foo foo) {        // do something    }}

@Transactional settings


@Transactional注解是元数据,指定的类,接口,或方法必须有事物语义;例如,当这个方法调用时开启一个新的read-only事物,挂起任何其他的事物。默认的@Transaction设置如下:

  • 传递性设置时PROPAGATION_REQUIRED.
  • 隔离性设置是ISOLATION_DEFAULT.
  • 事物是读/写的
  • 事物超时默认是底层事物系统默认的超时,或者不支持的话 默认为null
  • 任何运行异常触发回滚,但检查性异常不会

可以改变这些默认的设置;@transaction注解的不同属性总结如下:


Table 11.3 @

属性                 类型                   描述

value              String                 可选的限定符指定要使用的事物管理器

propagation  enum:Propagation   可选的传递性设置

isolation       enum:Isolation          可选的隔离级别

readOnly       boolean                          读/写 或者read-only事物

timeout         int(单位秒)                 事物超时

rollbackFor  Class对象数组,必须继承Throwable                  异常类的可选数组,必须引起回滚

rollbackForClassName   类名数组。类必须继承Throwable   异常类名的可选数组,必须引起回滚

noRollbackFor           类对象数组,继承Throwable                    异常类可选数组,不必引发回滚

noRollbackForClassName Sring 类名数组,必须继承Throwable  异常类的可选数组,不必引发回滚

           


当前不能直接控制事物名,名字将在事物监视器中显示。如果合适,(例如,WebLogic的事物监视器),和日志输出。对于声明式事物,事物名总是全限定类名+“。”+事物建议的类的方法名。例如,如果BusinessService类的handlePayment(..)方法开启了一个事物,事物名将是:com.foo.BusinessService.handlePayment.


@Transactional多事物管理器


大多数应用程序只需要一个单一的事物管理器,但是了可能出现这样的情形,你想在一个单一应用程序中使用多个独立的事物管理器。@Transactional注解的value属性可用于指定要使用的PlatformTransactionManager的标示。这个可以是bean名称或者事物管理bean的限定名称。例如,使用限定符号,下面的Java code:

public class TransactionalService {    @Transactional("order")    public void setSomething(String name) { ... }    @Transactional("account")    public void doSomething() { ... }}

在应用程序上下文中可以联合使用下面的事物管理器bean声明


tx:annotation-driven/>    <bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        ...        <qualifier value="order"/>    </bean>    <bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        ...        <qualifier value="account"/>    </bean>



在这个例子中,TransactionalService的两个方法将在不同的事物管理器下运行,分别由order和account限定名区分。默认的<tx:annotation-driven>目标bean名称transactionManager仍然会被使用,如果没有发现指定的PlatformTransactionManager。


通用的快捷的注解


如果发现在不同的方法上重复使用@Transactional相同的属性。那么Spring’s meta-annotation support允许你为你指定的使用情况定义通用便捷的注解。例如,定义下面的注解:

@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Transactional("order")public @interface OrderTx {}@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Transactional("account")public @interface AccountTx {}

这样允许我们这样写前面的额例子:

public class TransactionalService {    @OrderTx    public void setSomething(String name) { ... }    @AccountTx    public void doSomething() { ... }}

这里虽然使用语法定义了事物管理器限定符,但是了也包括了传播性行为,回滚规则,超时等


11.5.7 Transaction propagation  事物传递性

这部分描述了Spring中的事物传递性的一些语法。记住这里不是讲解事物传递属性,而是详细介绍事物传递语法。


在Spring管理的事物中,注意物理和逻辑事物的不同,和这些传递设置如何应用在这些不同之处。


Required


PROPAGATION_REQUIRED

当传播设置为PROPAGATION_REQUIRED,将为应用这些设置的方法创建逻辑事物范围。每个这样的逻辑事物范围可以分别指定rollback-only状态,其外部的事物范围与内部的事物范围会不干扰。当然了,在标准PROPAGATION_REQUIRED行为情况下,所有这些范围将被映射为相同的物理事物。这样一个内部事物范围的rollback-only标示设置确实影响了外部事物的提交。


然而,在内部事物范围设置为rollback-only标示的情况下,外部事物不能自己决定其回滚,并且回滚(由内部事物触发的范围)不可预测。此时抛出相应的UnexpectedRollbackException。这是可以预料的行为,这样事物调用者从来不会误导,假设执行了提交,当其不是真正的提交的时候。如果一个内部事物(外部事物注意不到)标示为一个read-only的事物,外部调用仍旧调用提交。外部调用者需要接受一个UnexpectedRollbackException来清晰的指定执行的回调。


RequiresNew



PROPAGATION_REQUIRES_NEW

PROPAGATION_REQUIRES_NEW相比较于PROPAGATION_REQUIRED,对于每个受影响的事物范围使用一个完全独立的事物。在那个情况下,指定的物理事物是不同的并且可以独立的提交或回滚,即外部事物不会受内部事物回滚状态的影响。


Nested(嵌套)

PROPAGATION_NESTED使用单一物理事物,其可以回滚到多个savepoints。这样局部的回滚允许一个内部事物范围触发器范围内的回滚,同时,外部事物能够继续忽略物理事物的一些操作,比如已经执行的回滚。这个设置一般映射到JDBC的savepoints,所以了只有在JDBC的资源事物中有效。


11.5.8 Advising transactional operations


假如你想执行事务性和基本设置文件的advice。那如何在<tx:annotation-driven/>上下文中起作用了?


当你调用updateFoo(Foo)方法时,你想看见如下的动作:

  • 配置设置文件方面启动
  • 事物建议执行
  • 建议对象方法的执行
  • 事物提交
  • 设置文件方面记录整个事物方法调用的确切期间



下面是一个简单的设置文件方面的例子。advice的顺序由Ordered接口控制。


package x.y;import org.aspectj.lang.ProceedingJoinPoint;import org.springframework.util.StopWatch;import org.springframework.core.Ordered;public class SimpleProfiler implements Ordered {    private int order;    // allows us to control the ordering of advice    public int getOrder() {        return this.order;    }    public void setOrder(int order) {        this.order = order;    }    // this method is the around advice    public Object profile(ProceedingJoinPoint call) throws Throwable {        Object returnValue;        StopWatch clock = new StopWatch(getClass().getName());        try {            clock.start(call.toShortString());            returnValue = call.proceed();        } finally {            clock.stop();            System.out.println(clock.prettyPrint());        }        return returnValue;    }}



<?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.xsd        http://www.springframework.org/schema/tx        http://www.springframework.org/schema/tx/spring-tx.xsd        http://www.springframework.org/schema/aop        http://www.springframework.org/schema/aop/spring-aop.xsd">    <bean id="fooService" class="x.y.service.DefaultFooService"/>    <!-- this is the aspect -->    <bean id="profiler" class="x.y.SimpleProfiler">        <!-- execute before the transactional advice (hence the lower order number) -->        <property name="order" value="1"/>    </bean>    <tx:annotation-driven transaction-manager="txManager" order="200"/>    <aop:config>            <!-- this advice will execute around the transactional advice -->            <aop:aspect id="profilingAspect" ref="profiler">                <aop:pointcut id="serviceMethodWithReturnValue"                        expression="execution(!void x.y..Service.(..))"/>                <aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>            </aop:aspect>    </aop:config>    <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>    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource"/>    </bean></beans>

上述配置的结果是一个fooservice bean按照预期的顺序的设置文件和事物切面。你可以按照相似方式配置其他的切面。


下面的例子显示了上述相同的配置,但是仅用了纯净的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.xsd        http://www.springframework.org/schema/tx        http://www.springframework.org/schema/tx/spring-tx.xsd        http://www.springframework.org/schema/aop        http://www.springframework.org/schema/aop/spring-aop.xsd">    <bean id="fooService" class="x.y.service.DefaultFooService"/>    <!-- the profiling advice -->    <bean id="profiler" class="x.y.SimpleProfiler">        <!-- execute before the transactional advice (hence the lower order number) -->        <property name="order" value="1"/>    </bean>    <aop:config>        <aop:pointcut id="entryPointMethod" expression="execution(* x.y..Service.(..))"/>        <!-- will execute after the profiling advice (c.f. the order attribute) -->        <aop:advisor advice-ref="txAdvice" pointcut-ref="entryPointMethod" order="2"/>        <!-- order value is higher than the profiling aspect -->        <aop:aspect id="profilingAspect" ref="profiler">            <aop:pointcut id="serviceMethodWithReturnValue"                    expression="execution(!void x.y..Service.(..))"/>            <aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>        </aop:aspect>    </aop:config>    <tx:advice id="txAdvice" transaction-manager="txManager">        <tx:attributes>            <tx:method name="get*" read-only="true"/>            <tx:method name="*"/>        </tx:attributes>    </tx:advice>    <!-- other <bean/> definitions such as a DataSource and a PlatformTransactionManager here --></beans>

上述配置的结果是一个fooService bean的配置文件并以那个顺序应用的事物切面。如果你想要执行的设置文件在的事物建议进去前,并在事物建议出来后,那么简单的设置设置文件 bean order属性的值,这样就比设置文件旧的值高点。


可以用相似的方式配置其他的切面。


11.5.9 使用AspectJ的@Transactional


也可以在Spring IoC容器外面通过一个AspectJ切面使用Spring框架的@Transaction。这样做,首先使用@Transaction注解你的类(通常是类中的方法),之后将与spring-aspectj.jar文件中的org.springframework.transaction.aspectj.AnnotationTransactionAspect关联。切面必须与一个事物管理器关联。当然了,你可以使用Spring IoC的容器来考虑切面的依赖注入。配置事物管理切面最简单的方式是使用<tx:annotation-driven/>并为aspectj指定mode属性。因为我们这里关注的是在Spring容器外部运行,所以了将显示如何程序化处理。


注意:

// construct an appropriate transaction managerDataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource());// configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methodsAnnotationTransactionAspect.aspectOf().setTransactionManager(txManager);


注意:当使用这个切面时,必须注解实现类(或者类中的方法),而不是实现类的接口。切面遵循Java的规则,接口上的注解是不能继承的。


类上的@Transactional为类中任何方法指定了默认的事物语法。


类中由@Transactional注解的方法重写了类注解的方法(如果有的话)。任何方法都能被注解,不管是否可见。


为使用AnnotationTransactionAspect编织你的应用程序,要不使用AspectJ构建你的应用程序,要不使用加载时间进行编织。





0 1
原创粉丝点击