Maven依赖
使用基于AspectJ的XML声明式事务管理需要引入AspectJ的包,我们使用Maven,依赖如下:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
声明式事务实现的例子
Spring Framework的声明式事务管理是建立在Spring的面向切面编程(aspect-oriented programming, AOP) 上的。为了说明清楚Spring的声明式事务到底要怎么做,我们借用Spring官方提供的示例代码。
假设我们有一个服务层接口,叫FooService,代码如下:
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();
}
}
我们定义的FooService接口的getFoo(String)和getFoo(String, String)必须在只读类型语义的事务上下文中执行,insertFoo(Foo)和updateFoo(Foo)必须在可读可写类型语义的事务上下文环境中执行。
我们的配置文件中的配置如下:
<?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"/>
<!-- 事务化配置(transactional advice) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- 事务语义(transactional semantics) -->
<tx:attributes>
<!-- 所有用get开头的方法都是只读的 -->
<tx:method name="get*" read-only="true"/>
<!-- 其他的方法使用默认的事务配置 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 确保上面的事务配置对FooService接口的所有操作有效 -->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</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>
<!-- 事务管理器(PlatformTransactionManager) -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 其他的<bean/>定义 -->
</beans>
我们来具体看一下这个配置文件的意思。
我们想让一个服务层对象,也就是fooService这个bean,支持事务。
关于事务语义的封装是定义在<tx:advice/>
里的。这里的<tx:advice/>
的定义就是说所有以get开头的方法都运行在只读的事务语义中,并且其他的所有方法都运行在默认的事务语义中。<tx:advice/>
标签的transaction-manager属性用来设置事务管理器(PlatformTransactionManager)bean的名称。transaction-manager属性如果不设置,那么默认就叫“transactionManager”。在这里我们指定的是叫txManager。
<aop:config/>
的定义确保了由txAdvice这个bean定义的事务配置在程序合适的切入点运行。首先需要定义一个pointcut来匹配FooService这个接口定义的任何操作。这里我们的这个pointcut起了id叫做“fooServiceOperation”,<aop:pointcut/>
中使用的表达式是一个AspectJ的pointcut表达式。然后用一个advisor将pointcut与txAdvice关联起来。这样,使用txAdvice定义的事务配置会在fooServiceOperation上面工作起来。
如果不光是想让这一个fooService拥有事务,而是想让整个service包都拥有事务,我们只需要把表达式改成expression="execution(* x.y.service.*.*(..))"
即可。
回滚声明式事务
让Spring Framework事务的基础构件知道事务需要进行回滚的推荐做法是在正在执行的代码的当前上下文中抛出 Exception。
在默认配置中,Spring Framework的事务基础构件只会在运行期、未检查的异常抛出时才会标记事务回滚。也就是说,当抛出的异常是RuntimeException或者其子类的实例时默认都是标记为回滚。事务的方法中抛出检查的异常时在默认情况下不会标记为回滚。
你可以自己配置哪些Exception的类型是需要标记为回滚的,例如:
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
也可以配置不回滚规则:
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
如果回滚规则和不回滚规则都配置了,最精确的将生效:
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
</tx:attributes>
</tx:advice>
也可以以编程的方式来回滚,不过这样具有侵入性,将代码同Spring Framework的事务基础构件紧耦合在了一起。
public void resolvePosition() {
try {
// some business logic...
} catch (NoProductInStockException ex) {
// trigger rollback programmatically
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
为不同的bean配置不同的事务
通过使用不同的pointcut-ref和advice-ref属性的值来定义不同的<aop:advisor/>
元素。
<aop:config>
<aop:pointcut id="defaultServiceOperation"
expression="execution(* x.y.service.*Service.*(..))"/>
<aop:pointcut id="noTxServiceOperation"
expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/>
<aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>
<aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>
</aop:config>
<!-- 这个bean是事务型的 -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- 这个bean也是事务型的,但是拥有不同的事务设置 -->
<bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/>
<tx:advice id="defaultTxAdvice">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<tx:advice id="noTxAdvice">
<tx:attributes>
<tx:method name="*" propagation="NEVER"/>
</tx:attributes>
</tx:advice>
<tx:advice/>
设置
<tx:advice/>
默认的设置如下:
传播行为是REQUIRED
隔离级别是DEFAULT
事务是可读可写
事务超时是使用系统底层组件的默认值,不支持超时的时候就没有超时设置
任何的RuntimeException都会触发回滚,checked Exception不会。
我们来看一下<tx:method/>
的设置
Attribute | Required? | Default | Description |
---|---|---|---|
name | Yes | 事务属性所关联的方法名称,可以使用通配符* ,如get* | |
propagation | No | REQUIRED | 事务的传播行为 |
isolation | No | DEFAULT | 事务的隔离级别 |
timeout | No | -1 | 事务的超时值,以秒为单位 |
read-only | No | false | 事务是不是只读的 |
rollback-for | No | 会触发回滚的Exception,多个则使用逗号分隔 | |
no-rollback-for | No | 不会触发回滚的Exception,多个则使用逗号分隔 |