Spring声明式事务(@Transactional)通过AOP实现过程
作者:冰糖心书房
Spring的声明式事务通过AOP代理技术实现,扫描标记了@Transactional的类或方法,为目标对象创建代理,方法调用时根据配置开启、提交或回滚事务,从而简化事务管理,提高代码复用性和可维护性
Spring 的声明式事务 (@Transactional) 是 AOP 技术最经典的应用之一。
其核心思想是:通过 AOP 代理,在业务代码执行前后,自动地开启、提交或回滚事务。
下面是详细的实现步骤和原理:
Step 1: 扫描与识别
当 Spring 容器启动时:
- 它会扫描所有的 Bean。
- 它会寻找被
@Transactional注解标记的类或方法。 - 一旦发现某个 Bean(例如
OrderService)中含有@Transactional方法,Spring 就会判定这个 Bean 需要事务增强。
Step 2: 创建代理对象
对于需要事务增强的 Bean,Spring 不会直接将原始的 OrderService 实例放入容器。相反,它会:
- 使用 AOP 框架(具体来说是
TransactionProxyFactoryBean)为这个OrderService创建一个代理对象(OrderServiceProxy)。 - 这个代理对象看起来和原始对象一模一样(实现了相同的接口或继承了相同的类),但它的内部包含了额外的事务拦截器 (Transaction Interceptor)。
- 最终,当其他组件(如 Controller)通过
@Autowired注入OrderService时,它们实际得到的是这个代理对象。
Step 3: 方法调用与拦截 (核心)
当你的代码调用一个被 @Transactional 注解的方法时(例如 orderServiceProxy.placeOrder()),这个调用过程会遵循一个典型的环绕通知 (@Around) 流程:
调用被代理对象拦截:
你的调用首先到达的是 OrderServiceProxy,而不是原始的 OrderService。
事务拦截器 (Transaction Interceptor) 被激活:
代理对象中的事务拦截器(可以看作一个环绕通知)开始工作。它的逻辑如下:
a. 【方法执行前】准备事务环境:
- * 获取
PlatformTransactionManager(事务管理器,如DataSourceTransactionManager)。 - * 检查当前是否存在一个已激活的事务。
- * 根据
@Transactional注解中配置的传播行为 (Propagation) 来决定下一步操作。例如: - *
REQUIRED(默认):如果当前没事务,就开启一个新事务 (connection.setAutoCommit(false));如果已有事务,就加入它。 - *
REQUIRES_NEW:挂起当前事务(如果存在),并总是开启一个全新的事务。 - * 根据注解配置设置事务的隔离级别 (Isolation)、超时时间 (Timeout)、只读状态 (Read-Only) 等。
b. 【调用目标方法】执行核心业务逻辑:
- * 在
try代码块中,事务拦截器会调用原始的OrderService对象的placeOrder()方法。 - *
orderRepository.save(order); - *
stockService.decreaseStock(order.getProductId()); - * 你的所有业务代码都在这个事务的保护下执行。
c. 【方法成功返回后】提交事务:
- * 如果目标方法成功执行完毕,没有抛出任何异常,
try块正常结束。 - * 事务拦截器会捕获到这个成功的状态,然后调用
transactionManager.commit()来提交事务。
d. 【方法抛出异常后】回滚事务:
- * 如果目标方法在执行过程中抛出了异常,这个异常会被
catch块捕获。 - * 事务拦截器会检查这个异常的类型。根据
@Transactional注解的回滚规则 (Rollback Rules): - * 默认情况下,如果抛出的是
RuntimeException或Error,事务拦截器会调用transactionManager.rollback()来回滚事务。 - * 如果抛出的是已检查异常 (Checked Exception),默认不会回滚(这个行为可以配置)。
- * 回滚后,异常会继续向上层抛出,以便调用方可以感知到错误。
图解实现原理
+----------------+
| Controller |
+----------------+
| 1. 调用 orderService.placeOrder()
v
+-----------------------------------------------------------------------------+
| OrderService 代理对象 (Proxy) |
| |
| +---------------------------------------------------------------------+ |
| | 事务拦截器 (Transaction Interceptor - Around Advice) | |
| | | |
| | 2. 【方法执行前】 | |
| | - 获取事务管理器 | |
| | - 根据传播行为,开启新事务 (connection.setAutoCommit(false)) | |
| | | |
| | 3. 【调用目标方法】try { | |
| | target.placeOrder(); <---------------------------------+ | |
| | } | | |
| | | | |
| | 4a. 【成功返回后】catch (no exception) { | | |
| | transactionManager.commit(); // 提交事务 | | |
| | } | | |
| | | | |
| | 4b. 【抛出异常后】catch (RuntimeException | Error) { | | |
| | transactionManager.rollback(); // 回滚事务 | | |
| | throw ex; // 继续抛出异常 | | |
| | } | | |
| | | |
| +---------------------------------------------------------------------+ |
| |
+-----------------------------------------------------------------------------+
^ |
| 5. 最终结果或异常返回给 Controller |
| |
| +--------------------------------------------------------+
| |
v v
+--------------------------+
| OrderService 目标对象 |
| (包含纯粹的业务逻辑) |
+--------------------------+
总结
- 核心技术:Spring AOP 的环绕通知 (
@Around)。
关键角色:
- 代理对象 (Proxy):拦截方法调用。
- 事务拦截器 (Transaction Interceptor):实现了具体的事务管理逻辑。
- 事务管理器 (PlatformTransactionManager):执行真正的事务操作(开启、提交、回滚)。
工作流程:
- 代理拦截:调用被代理对象拦截。
- 事务开启:在目标方法执行前,根据配置开启事务。
- 业务执行:调用原始目标对象的业务方法。
- 事务提交/回滚:根据业务方法的执行结果(成功或异常),决定是提交还是回滚事务。
通过这种方式,AOP 完美地将重复、通用的事务管理代码从核心业务逻辑中剥离出来,实现了高度的解耦和代码的整洁。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
