java——@Transactional 在哪些情况下会失效?
@Transactional注解失效原因分类
@Transactional注解在Spring框架中用于声明式事务管理,确保数据的一致性和可靠性。然而,在某些情况下,@Transactional注解可能会失效。以下是一些常见的情况及原因:
- 方法访问权限设置不当:
- @Transactional注解通常只能应用于public方法上。如果将其应用于protected、private或包级私有方法上,由于Spring的代理机制无法拦截这些方法的调用,因此事务注解将失效。
- 方法内部调用:
- 当一个事务方法在同一个类中调用另一个事务方法时,如果调用是通过this关键字进行的,那么被调用的方法将不会通过Spring的代理对象,因此@Transactional注解将不会生效。
- 事务管理器配置错误:
- 如果Spring容器中配置了多个事务管理器,但在使用@Transactional注解时没有明确指定事务管理器,可能会导致Spring使用默认的事务管理器,而这个默认的事务管理器可能不适用于当前的操作,从而导致事务注解失效。
- 方法内部捕捉异常:
- 在使用@Transactional注解的方法中,如果内部捕获了可能导致事务回滚的异常,并且没有重新抛出一个Spring框架能够识别的运行时异常或声明式异常,那么事务管理器将无法感知到异常,从而可能导致事务不会回滚。
- final修饰的方法:
- 如果使用final关键字修饰了方法,那么由于该方法不能被重写,Spring的代理机制将无法对其应用@Transactional注解,因此事务将失效。
- 静态方法:
- 静态方法同样无法通过动态代理来应用@Transactional注解,因为静态方法不属于类的实例方法,而是属于类本身。
- 未被Spring管理的类:
- 如果一个类没有被Spring管理(即没有使用@Controller、@Service、@Component、@Repository等注解进行标注),那么该类中的方法即使使用了@Transactional注解也不会生效。
- 传播特性配置错误:
- 在使用@Transactional注解时,可以指定propagation参数来定义事务的传播行为。如果传播特性配置错误(例如设置为Propagation.NEVER,而当前存在事务),则事务将不会生效。
- 表不支持事务:
- 如果使用的数据库表不支持事务(例如,某些类型的存储引擎或数据库系统不支持事务),那么即使使用了@Transactional注解,事务也不会生效。
- 多线程调用:
- 在多线程环境下,每个线程都有自己的数据库连接和事务上下文。因此,如果一个事务方法被另一个线程调用,那么它们将不会共享同一个事务上下文,从而导致事务失效。
避免@Transactional注解失效策略
- 方法是public的。
- 避免在同一个类中直接调用其他事务方法(可以通过注入自身的方式来解决)。
- 正确配置事务管理器。
- 不要在事务方法内部捕获并处理可能导致事务回滚的异常(或者重新抛出一个Spring框架能够识别的异常)。
- 避免使用final和static修饰事务方法。
- 确保类被Spring管理。
- 正确配置事务的传播特性。
- 使用支持事务的数据库表和存储引擎。
- 在多线程环境下,确保事务方法在同一个线程中执行。
通过遵循这些原则,可以最大程度地确保@Transactional注解在Spring框架中的正确性和有效性。
底层原码
@Transactional
注解是 Spring 框架中用于声明式事务管理的关键注解。它通过 AOP(面向切面编程)机制在方法调用前后插入事务管理逻辑。要理解 @Transactional
的底层实现,我们需要深入到 Spring 的源代码中。
1. @Transactional
注解
@Transactional
是一个元注解,定义在 org.springframework.transaction.annotation
包下:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
// 定义了各种属性,如传播行为、隔离级别等
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
2. TransactionInterceptor
类
@Transactional
注解的实现依赖于 TransactionInterceptor
类,这是一个实现了 MethodInterceptor
接口的拦截器。TransactionInterceptor
负责在方法调用前开启事务,在方法调用后提交或回滚事务。
TransactionInterceptor
的关键方法
invoke
方法:这是MethodInterceptor
接口的核心方法,负责拦截方法调用并处理事务逻辑。
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 获取事务属性
TransactionAttributeSource tas = getTransactionAttributeSource();
TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(invocation.getMethod(), invocation.getClass()) : null);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
// 如果没有事务属性,则直接调用目标方法
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, invocation);
Object retVal;
try {
// 执行目标方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 处理异常
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
// 提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
// 使用回调方式处理事务
else {
// It's a CallbackPreferringPlatformTransactionManager: pass a callback in order to
// avoid unnecessary intermediate transactions and allow for eager triggering of actual
// suspension at the head of the chain.
return ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, invocation, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
}
});
}
}
3. TransactionAttributeSource
接口
TransactionAttributeSource
接口用于获取方法上的事务属性。Spring 通常使用 AnnotationTransactionAttributeSource
来解析 @Transactional
注解。
4. PlatformTransactionManager
接口
PlatformTransactionManager
是 Spring 中用于管理事务的核心接口。不同的数据访问技术有不同的实现,例如 DataSourceTransactionManager
、JpaTransactionManager
等。
5. 事务管理流程
-
创建代理对象:
- Spring 使用 JDK 动态代理或 CGLIB 代理来创建目标对象的代理。
- 代理对象在方法调用前后插入事务管理逻辑。
-
拦截方法调用:
TransactionInterceptor
在方法调用前检查是否有@Transactional
注解,并根据注解中的配置决定是否需要开启新的事务。- 如果需要开启新事务,则调用
PlatformTransactionManager
的getTransaction
方法开始一个新的事务。 - 执行目标方法。
-
处理结果和异常:
- 如果目标方法执行成功且没有抛出异常,则调用
PlatformTransactionManager
的commit
方法提交事务。 - 如果目标方法抛出异常,则调用
PlatformTransactionManager
的rollback
方法回滚事务。
- 如果目标方法执行成功且没有抛出异常,则调用