当您对 Spring 的基于注解方式的实现步骤和事务内在实现机制有较好的理解之后,就会更好的使用注解方式的事务管理,避免当系统抛出异常,数据不能回滚的问题。
需要注意下面三种 propagation 可以不启动事务。本来期望目标方法进行事务管理,但若是错误的配置这三种 propagation,事务将不会发生回滚。
默认情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常)或者 Error,则 Spring 将回滚事务;除此之外,Spring 不会回滚事务。
如果在事务中抛出其他类型的异常,并期望 Spring 能够回滚事务,可以指定 rollbackFor。例:
@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class)
通过分析 Spring 源码可以知道,若在目标方法中抛出的异常是 rollbackFor 指定的异常的子类,事务同样会回滚。
1
2
3
4
5
6
7
8
9
10
11
|
private int getDepth(Class> exceptionClass, int depth) {
if (exceptionClass.getName().contains(this.exceptionName)) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
if (exceptionClass == Throwable.class) {
return -1;
}
return getDepth(exceptionClass.getSuperclass(), depth + 1);
}
|
只有@Transactional 注解应用到 public 方法,才能进行事务管理。这是因为在使用 Spring AOP 代理时,Spring 在调用在图 1 中的 TransactionInterceptor 在目标方法执行前后进行拦截之前,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取表 1. @Transactional 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法。
1
2
3
4
5
|
protected TransactionAttribute computeTransactionAttribute(Method method,
Class> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;}
|
这个方法会检查目标方法的修饰符是不是 public,若不是 public,就不会获取@Transactional 的属性配置信息,最终会造成不会用 TransactionInterceptor 来拦截该目标方法进行事务管理。
在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问题。若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,不会发生回滚。见清单 5 举例代码展示。
1
2
3
4
5
6
7
8
9
10
11
12
|
@Service
-->public class OrderService {
private void insert() {
insertOrder();
}
@Transactional
public void insertOrder() {
//insert log info
//insertOrder
//updateAccount
}
}
|
insertOrder 尽管有@Transactional 注解,但它被内部方法 insert 调用,事务被忽略,出现异常事务不会发生回滚。
上面的两个问题@Transactional 注解只应用到 public 方法和自调用问题,是由于使用 Spring AOP 代理造成的。为解决这两个问题,使用 AspectJ 取代 Spring AOP 代理。
需要将下面的 AspectJ 信息添加到 xml 配置信息中。
1
2
3
4
5
6
7
8
9
10
|
<
tx:annotation-driven
mode
=
"aspectj"
/>
<
bean
id
=
"transactionManager"
class
=
"org.springframework.jdbc.datasource.DataSourceTransactionManager"
>
<
property
name
=
"dataSource"
ref
=
"dataSource"
/>
bean
>
bean
class
=
"org.springframework.transaction.aspectj.AnnotationTransactionAspect"
factory-method
=
"aspectOf"
>
<
property
name
=
"transactionManager"
ref
=
"transactionManager"
/>
bean
>
|
同时在 Maven 的 pom 文件中加入 spring-aspects 和 aspectjrt 的 dependency 以及 aspectj-maven-plugin。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<
dependency
>
<
groupId
>org.springframework
groupId
>
<
artifactId
>spring-aspects
artifactId
>
<
version
>4.3.2.RELEASE
version
>
dependency
>
<
dependency
>
<
groupId
>org.aspectj
groupId
>
<
artifactId
>aspectjrt
artifactId
>
<
version
>1.8.9
version
>
dependency
>
<
plugin
>
<
groupId
>org.codehaus.mojo
groupId
>
<
artifactId
>aspectj-maven-plugin
artifactId
>
<
version
>1.9
version
>
<
configuration
>
<
showWeaveInfo
>true
showWeaveInfo
>
<
aspectLibraries
>
<
aspectLibrary
>
<
groupId
>org.springframework
groupId
>
<
artifactId
>spring-aspects
artifactId
>
aspectLibrary
>
aspectLibraries
>
configuration
>
<
executions
>
<
execution
>
<
goals
>
<
goal
>compile
goal
>
<
goal
>test-compile
goal
>
goals
>
execution
>
executions
>
plugin
>
|
通过本文的介绍,相信读者能够清楚的了解基于@Transactional 注解的实现步骤,能够透彻的理解的 Spring 的内部实现机制,并有效的掌握相关使用注意事项,从而能够正确而熟练的使用基于@Transactional 注解的事务管理方式。