# 失效场景
# 1、修饰非public方法
@Service
public class RoleService extends ServiceImpl<RoleMapper, Role> {
@Transactional
void delete() {
//处理异常回滚,不会生效
}
}
2
3
4
5
6
7
1. 为什么修饰非public方法,事务没有效果?
要回答这个问题,首先要了解Transaction工作原理,spring会找到bean中标注该注解的方法,
然后(通过动态代理或者cglib)生成代理对象,使用try catch包裹整个方法,在注解作用的方法前后分别开启事务和提交事务,
如果被包裹方法发生异常,则在catch中回滚事务。
知道注解的工作原理之后,那么就可以进一步了解为什么对非public的方法失效,原因在于spring在遍历bean中带有@Transaction方法时进行了
判断,具体是在org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#computeTransactionAttribute中
/**
* Same signature as {@link #getTransactionAttribute}, but doesn't cache the result.
* {@link #getTransactionAttribute} is effectively a caching decorator for this method.
* <p>As of 4.1.8, this method can be overridden.
* @since 4.1.8
* @see #getTransactionAttribute
* 与getTransactionAttribute方法签名相同,但是不缓存结果,getTransactionAttribute缓存本方法返回的结果,可以得知computeTransactionAttribute被getTransactionAttribute在缓存为空时调用。
*/
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required. > 此处表明可以回答问题1,非public的方法不进行处理,直接返回null.allowPublicMethodsOnly方法默认返回true
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// ...剩下处理忽略不计
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2、嵌套调用
同一个类中调用@Transaction修饰的方法
@Service
public class RoleService extends ServiceImpl<RoleMapper, Role> {
@Resource
RoleMapper roleMapper;
@Transactional
public void delete() {
//处理异常回滚
}
/**
* 直接进行方法调用不会回滚
*/
public void handle() {
delete();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2. 为什么没有注解的方法调用带有注解的delete方法,事务就失效了?
通过问题1可以知道注解生效的原理,在于生成代理对象,容器负责开启和提交或者回滚事务,在handle中调用delete()方法,其实可以看做为
this.delete(),其实是通过实例对象调用,也就是非代理对象,那也就非代理对象生成的增强事务方法,所以事务失效。只有当事务方法被当前类以外的代码
调用时,才会由spring生成的代理对象来管理,或者在当前bean注入自身,使用注入对象来调用。
# 3、事务传播属性
事务传播属性配置同样会影响事务,事务的传播级别针对调用者而言,作用于标注方法之上。
- Propagation.PROPAGATION_SUPPORTS
如果当前存在事务,则加入该事务;如果当前不存在事务,则新建一个事务。(默认传播级别)
Service_A{
@Transaction
public void a()
}
Service_B{
@Transaction
public void b()
}
Service_C{
@Resource
Service_A a;
@Resource
Service_B b;
public void c1(){
//此处由于c1不存在事务,则a和b各自另起一个事务且互相不影响,但是由于代理方法在回滚事务后,
//再次抛出了异常,所以后面的方法依旧会阻断
a.a();
b.b()
}
@Transaction
public void c2(){
//此处由于c2已经存在事务,则a和b直接加入c2事务;
a.a();
b.b()
}
}
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
调用栈如下
最后处理事务方法
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final TransactionManager tm = determineTransactionManager(txAttr);
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new TransactionUsageException(
"Unsupported annotated transaction on suspending function detected: " + method +
". Use TransactionalOperator.transactional extensions instead.");
}
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
if (adapter == null) {
throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
method.getReturnType());
}
return new ReactiveTransactionSupport(adapter);
});
return txSupport.invokeWithinTransaction(
method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
}
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception **回滚事务重点在这里**
completeTransactionAfterThrowing(txInfo, ex);
//**重新抛出异常**
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
Object result;
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
try {
Object retVal = invocation.proceedWithInvocation();
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
return retVal;
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
}
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
流程:
TransactionInterceptor事务拦截器在标注事务方法处拦截,然后DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,这个方法会获取Transactional 注解的事务配置信息。他会首先校验事务方法的修饰符是不是public,不是 public则不会获取@Transactional 的属性配置信息。42行回滚事务,44行重新抛出异常。
- Propagation.SUPPORTS
如果当前存在事务,则加入该事务;如果当前不存在事务,则默认本方法也没有事务。
- Propagation.NOT_SUPPORTED
被注解的方法处于无事务状态,即使caller存在事务。
- Propagation.MANDATORY
要求caller必须有事务,否则抛出异常。
- Propagation.NEVER
如果caller存在事务则抛出异常。
- Propagation.REQUIRES_NEW
不管caller是否有事务,当前方法都新建一个独立的事务。