Spring事务失效浅析

# 失效场景

# 1、修饰非public方法

@Service
public class RoleService extends ServiceImpl<RoleMapper, Role> {
    @Transactional
    void delete() {
        //处理异常回滚,不会生效
    }
}
1
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;
		}
		// ...剩下处理忽略不计
	}

1
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();
    }
}    
1
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、事务传播属性

事务传播属性配置同样会影响事务,事务的传播级别针对调用者而言,作用于标注方法之上。

  1. 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()
    }
}
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

调用栈如下 img.png 最后处理事务方法

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;
		}
	}
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
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行重新抛出异常。

  1. Propagation.SUPPORTS

  如果当前存在事务,则加入该事务;如果当前不存在事务,则默认本方法也没有事务。

  1. Propagation.NOT_SUPPORTED

  被注解的方法处于无事务状态,即使caller存在事务。

  1. Propagation.MANDATORY

  要求caller必须有事务,否则抛出异常。

  1. Propagation.NEVER

  如果caller存在事务则抛出异常。

  1. Propagation.REQUIRES_NEW

  不管caller是否有事务,当前方法都新建一个独立的事务。