Spring 框架中的@Transactional
注解是用来简化数据库事务管理的重要工具。下面详细介绍它实现数据库事务的原理。
核心原理概述
@Transactional
主要借助 Spring 的 AOP(面向切面编程)机制来达成事务管理。当一个方法被@Transactional
注解修饰时,Spring 会在该方法执行前后添加事务相关的处理逻辑,以保证方法内的数据库操作在一个统一的事务环境中进行。
具体实现步骤
1. 配置事务管理器
在 Spring 应用里,你得先配置一个事务管理器,这个管理器会和具体的数据源关联起来。例如,使用DataSourceTransactionManager
管理基于 JDBC 的事务。以下是 Java 配置示例:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@Configuration
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
2. 启用事务注解支持
在 Spring 配置类上添加@EnableTransactionManagement
注解,以此开启@Transactional
注解的功能。
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
public class AppConfig {
// 其他配置
}
@EnableTransactionManagement
注解的作用就是开启这种声明式事务管理功能。
注解属性
- mode:用于指定 AOP 代理模式,有
AdviceMode.PROXY
(默认值,使用 Spring AOP 代理)和AdviceMode.ASPECTJ
(使用 AspectJ 进行切面织入)两种可选值。 - proxyTargetClass:仅在
mode
为AdviceMode.PROXY
时有效,若为true
则使用 CGLIB 代理,为false
则使用 JDK 动态代理。 - order:指定事务增强器的执行顺序。
工作流程
-
导入配置类:
@EnableTransactionManagement
注解借助@Import
注解导入TransactionManagementConfigurationSelector
类。 -
配置类选择:
TransactionManagementConfigurationSelector
会依据 AOP 代理模式来选择配置类。若使用 JDK 动态代理,就导入ProxyTransactionManagementConfiguration
类;若使用 CGLIB 代理,则导入AspectJTransactionManagementConfiguration
类。 -
注册事务增强器:所选的配置类会注册
BeanFactoryTransactionAttributeSourceAdvisor
事务增强器。这个增强器会扫描带有@Transactional
注解的方法,为其创建代理对象。
3. AOP 代理创建
当 Spring 容器启动时,它会扫描所有带有@Transactional
注解的类和方法,然后借助 AOP 为这些类或方法创建代理对象。Spring 提供了两种代理方式:JDK 动态代理和 CGLIB 代理。
- JDK 动态代理:若目标对象实现了接口,Spring 默认会使用 JDK 动态代理。
- CGLIB 代理:若目标对象没有实现接口,Spring 会使用 CGLIB 代理。
4. 事务拦截器插入
在代理对象中,Spring 会插入一个事务拦截器(TransactionInterceptor
)。这个拦截器会在目标方法执行前后进行事务管理。
@SuppressWarnings("serial")
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
...
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
}
执行拦截器invoke方法的时候,主要业务逻辑在invokeWithTransaction方法里。
5. 事务拦截器工作流程
- 方法调用:当调用被
@Transactional
注解修饰的方法时,实际上调用的是代理对象的方法。 - 事务开启:事务拦截器会根据
@Transactional
注解的配置,从事务管理器获取一个事务,并开启这个事务。 - 目标方法执行:在事务开启后,拦截器会调用目标对象的实际方法,执行数据库操作。
- 事务提交或回滚:
- 正常情况:若目标方法正常执行完毕,没有抛出异常,事务拦截器会提交事务。
- 异常情况:若目标方法抛出了异常,事务拦截器会根据
@Transactional
注解的rollbackFor
和noRollbackFor
属性来判断是否需要回滚事务。若需要回滚,就会执行回滚操作。
invokeWithTransaction源代码如下,讲述了事务的具体执行流程。
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager cpptm)) {
// 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 && txAttr != null) {
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null) {
if (retVal instanceof Future<?> future && future.isDone()) {
try {
future.get();
}
catch (ExecutionException ex) {
Throwable cause = ex.getCause();
Assert.state(cause != null, "Cause must not be null");
if (txAttr.rollbackOn(cause)) {
status.setRollbackOnly();
}
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
else if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
retVal = invocation.proceedWithInvocation(); 执行原始业务逻辑。
completeTransactionAfterThrowing(txInfo, ex); 处理碰到异常事务回滚。
commitTransactionAfterReturning(txInfo); 正常流程执行完成,提交事务。
示例代码
以下是一个简单的示例,展示如何使用@Transactional
注解:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void createUser(User user) {
userRepository.save(user);
// 可能会抛出异常的代码
if (user.getName() == null) {
throw new RuntimeException("用户名不能为空");
}
}
}
在上述示例中,createUser
方法被@Transactional
注解修饰。若方法执行过程中抛出RuntimeException
,事务会回滚,user
不会被保存到数据库。
总结
@Transactional
注解通过 Spring 的 AOP 机制,在目标方法执行前后插入事务管理逻辑,从而实现了数据库事务的自动管理。它大大简化了事务处理的代码,提升了开发效率。