Spring 如何使用 AOP 实现声明式事务管理?

Dell R730安装Windows Server 2008 R2 一、RAID驱动准备  在安装Windows server2008 R2操作系统时,如果没有发现硬盘,请将下面的阵列卡驱动先解压(在下载的.exe文件,双击点下一步会自动解压),解压后把那文件夹复制到U盘中。阵列卡 PERC9 H330 - H730 - H730P - H830 Controllers for Windows Server 2008 R2二、系统安装方法方法一:光盘安装  ①请您... 阅读详情

Spring 使用 AOP 实现声明式事务管理是其最强大的功能之一。下面我将详细拆解这个过程,从高层概念到底层原理。


一、 核心思想:代理模式 + AOP

想象一下,你是一位非常重要的业务专家(你的 Service 层代码),你的工作是处理核心业务逻辑(比如转账、更新用户信息)。但每次处理业务前,你都需要做一些准备工作(开启事务),结束后还要做一些收尾工作(提交或回滚事务)。这些准备和收尾工作繁琐、重复,而且和你的核心业务关系不大。

声明式事务管理的目标就是:让你这位专家只专注于核心业务,把所有繁琐的事务管理工作交给一个“管家”来处理。

这个“管家”就是 Spring AOP 创建的代理对象 (Proxy)。

AOP (Aspect-Oriented Programming, 面向切面编程) 在这里扮演的角色就是:

  1. 定义“切面” (Aspect):将事务管理(开启、提交、回滚)这个“横切关注点”从业务代码中抽离出来,形成一个独立的模块。
  2. 定义“切点” (Pointcut):精确地定义这个“管家”应该在哪些方法执行前后介入。在 Spring 中,这通常是通过 @Transactional 注解来指定的。
  3. 创建“代理” (Proxy):Spring 不会直接返回你的原始业务对象,而是会创建一个该对象的代理。所有对业务对象的调用都会先经过这个代理。

二、 详细工作流程(一步步拆解)

让我们跟踪一个对 @Transactional 方法的调用,看看 Spring 内部到底发生了什么。

场景:一个 UserController 调用一个 UserService 的 updateUser() 方法,该方法被 @Transactional 注解。

// 业务接口
public interface UserService {
    void updateUser(User user);
}

// 业务实现类
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    @Transactional // <--- 关键注解
    public void updateUser(User user) {
        // 核心业务逻辑
        userRepository.update(user);
        // 假设这里如果出错了,需要回滚
        if (user.getName() == null) {
            throw new IllegalArgumentException("Username cannot be null");
        }
    }
}

// 调用方
@RestController
public class UserController {
    @Autowired
    private UserService userService; // <--- 注意:这里注入的其实是代理对象

    @PostMapping("/user/update")
    public void updateUser(@RequestBody User user) {
        userService.updateUser(user);
    }
}
阶段一:应用启动时 - 创建代理
  1. Bean 扫描:Spring 容器在启动时,会扫描所有的 Bean。
  2. 识别 @Transactional:当 Spring 扫描到 UserServiceImpl 时,它会发现这个类或其方法上存在 @Transactional 注解。
  3. 创建代理对象:Spring 认识到这个 Bean 需要被事务“增强”(Advised)。它不会直接创建 UserServiceImpl 的实例并放入容器,而是会通过 AOP 框架为它创建一个代理对象。
    • 如果 UserServiceImpl 实现了接口(如本例中的 UserService),Spring 默认会使用 JDK 动态代理来创建一个实现了 UserService 接口的代理类。
    • 如果 UserServiceImpl 没有实现任何接口,Spring 会使用 CGLIB 来创建一个 UserServiceImpl 的子类作为代理。
  4. 注入代理:当 UserController 通过 @Autowired 请求注入 UserService 时,Spring 容器会将上一步创建的那个代理对象注入给它,而不是原始的 UserServiceImpl 对象。UserController 对此毫不知情,它以为自己拿到的就是普通的 UserService。
阶段二:方法调用时 - 事务拦截
  1. 调用入口:UserController 调用 userService.updateUser(user)。

  2. 命中代理:这个调用首先命中的是代理对象的 updateUser 方法,而不是原始对象的。

  3. 事务拦截器 (TransactionInterceptor) 生效:代理对象的这个方法中包含了 AOP 的“通知”(Advice)。对于事务来说,这个通知的具体实现就是 TransactionInterceptor。这个拦截器就像一个警卫,拦住了这次调用。

  4. 开启事务:

    • TransactionInterceptor 首先会解析 updateUser 方法上 @Transactional 注解的属性(如传播行为 propagation、隔离级别 isolation、是否只读 readOnly 等)。
    • 它向事务管理器 (PlatformTransactionManager) 请求一个事务。
    • 事务管理器(例如 DataSourceTransactionManager)会从数据库连接池中获取一个连接 (Connection),然后执行 connection.setAutoCommit(false),这标志着事务的正式开始。
  5. 调用原始方法:事务成功开启后,TransactionInterceptor 会通过反射调用原始 UserServiceImpl 对象的 updateUser 方法。

  6. 执行业务逻辑:此时,才真正开始执行你编写的核心业务代码,比如 userRepository.update(user)。所有这些数据库操作都将在上一步获取的那个被 Spring 管理的 Connection 上执行。

阶段三:方法执行后 - 提交或回滚

当原始的 updateUser 方法执行完毕后,控制权返回到 TransactionInterceptor。

  1. 正常执行完毕 (没有抛出异常):

    • 拦截器捕获到方法正常返回。
    • 它会通知事务管理器提交 (commit) 事务。
    • 事务管理器调用 connection.commit(),将本次事务中的所有数据库操作永久保存。
  2. 抛出异常:

    • updateUser 方法内部抛出了一个异常(默认是 RuntimeException 或 Error)。
    • 拦截器在其 try-catch 块中捕获到这个异常。
    • 它会通知事务管理器回滚 (rollback) 事务。
    • 事务管理器调用 connection.rollback(),撤销本次事务中的所有数据库操作。
    • 最后,拦截器会将捕获到的异常继续向上抛出,以便上层调用者(如 UserController 或全局异常处理器)能够感知到。
阶段四:收尾

无论事务是提交还是回滚,事务管理器最终都会将数据库连接释放回连接池。


三、 核心组件总结

  • @Transactional:元数据,告诉 AOP 在哪里以及如何应用事务。它本身不包含任何逻辑。
  • AOP 代理 (Proxy):由 Spring 动态创建的“管家”对象,是事务逻辑的实际入口。它包装了原始的业务对象。
  • 事务拦截器 (TransactionInterceptor):AOP 通知(Advice)的具体实现,是代理对象中的核心逻辑。它负责在目标方法调用前后开启、提交或回滚事务。
  • 事务管理器 (PlatformTransactionManager):一个统一的事务管理接口。Spring 通过它来适配不同的数据源技术(如 JDBC, JPA/Hibernate, JTA)。它负责执行真正的事务操作(begin, commit, rollback)。

四、 为什么 @Transactional 在某些情况下会失效?

理解了以上原理,就很容易明白为什么 @Transactional 会在以下情况失效:

  1. 方法不是 public 的:CGLIB 代理是通过继承来实现的,而 private 或 protected 方法无法被子类重写(override),所以 AOP 无法拦截。JDK 动态代理基于接口,更不存在非 public 方法。
  2. 同一个类中的方法调用(this 调用):
    @Service
    public class UserServiceImpl implements UserService {
        public void entryMethod() {
            this.updateUser(user); // <--- 失效!
        }
    
        @Transactional
        public void updateUser(User user) {
            // ...
        }
    }
    
    当外部调用 entryMethod() 时,进入的是 UserServiceImpl 的原始对象(如果 entryMethod 没有被代理)。然后 this.updateUser() 是一个对象内部的直接调用,它绕过了代理对象,直接调用了原始对象的 updateUser 方法,因此事务拦截器根本没有机会介入。
  3. 异常被 catch 掉了:
    @Transactional
    public void updateUser(User user) {
        try {
            // ... 抛出 RuntimeException 的代码
        } catch (Exception e) {
            // 异常被吞了,没有继续抛出
        }
    }
    
    事务拦截器是通过捕获异常来决定是否回滚的。如果异常被你的 try-catch 块“消化”了,拦截器就认为方法是正常执行完毕的,于是它会提交事务,而不是回滚。

通过这个机制,Spring 完美地将业务逻辑和非功能性的事务管理代码解耦,让开发者能更专注于业务价值的实现,代码也变得更加清晰和易于维护。

STM32F407中文手册 STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。 立即下载

相关推荐

从想法到零件:手把手拆解一个汽车功能如何从PRD写到SSTS和CTS

本文详细拆解了汽车电子需求从市场文档(MRD)到零部件技术规范(CTS)的完整流程,以雨天自动关窗功能为例,展示了PRD、SSTS和CTS的编写要点与交互设计。文章重点解析了系统分解的艺术和零部件落地的精确参数要求,为汽车电子工程师提供了从概念到实现的全流程实践指南。

weixin_30300225的博客 481

事务管理&AOP

事务是一组操作的集合,是一个不可分割的工作单位,它会把所有操作当成一个整体,向数据库进行提交或者是撤销的操作请求,所以这组操作要么同时成功,要么同时失败。AOP英文全称:Aspect Oriented Programming(面向切面编程、面向方面编程)面向切面编程其实就是面向特定方法编程。又引入了一个新问题,什么是面向特定方法的编程?比如有一个项目,项目中开发了很多的业务功能。

小七的张张的博客 1208

Windows 24H2下eNSP AR_40启动失败的终极修复指南

本文针对Windows 11 24H2更新后eNSP AR_40路由器启动失败的问题,提供了终极修复方案。核心在于解决系统强制虚拟化安全(VBS/Hyper-V)与老版模拟软件的兼容性冲突。指南详细介绍了安装关键系统更新KB5053656、精细调整Windows虚拟化功能以及后续排错步骤,帮助用户在24H2系统上稳定运行eNSP进行网络实验。

weixin_29233333的博客 507

AOP-事务

AOP的作用:在程序运行期间在不修改源代码的基础上对已有方法进行增强(无侵入性: 解耦)

Mr_FunnyNiu的博客 691

keil软件调试(Debug)仿真教程(软件调试和硬件调试的区别)及常用调试按键详解

单片机的调试分为两种,一种是使用软件模拟调试,第二种是硬件调试,两种调试方式各有不同,软件模拟调试有误差,而硬件调试,借用仿真器调试是嵌入式开发中常用方法。

weixin_51624736的博客 3万+

保姆级教程:用STM32CubeMX配置GD32的CAN总线(含异常处理)

本文提供了一份详细的STM32CubeMX配置GD32 CAN总线开发指南,涵盖从HAL库移植到异常处理的全过程。重点解析GD32与STM32在CAN总线初始化、报文发送优先级处理等方面的关键差异,并提供优化代码维护与版本控制的实用策略,帮助开发者高效完成项目迁移。

onion的博客 830

Spring(三)异步调用、定时器、缓存

@Async、@Scheduled、@Cacheable

HK的博客 4057

Saturn PCB Toolkit V8.02 实战:5分钟完成10A电源路径的走线与过孔通流验证

本文详细介绍了如何使用Saturn PCB Toolkit V8.02快速验证10A电源路径的走线与过孔通流能力。通过实战案例和参数配置指南,帮助工程师解决大电流PCB设计中的温升失控、过孔瓶颈等核心挑战,提升设计效率和可靠性。

weixin_34194829的博客 451

使用aop来实现事务功能(日志)

通俗讲, AOP思想就是把中间的非主流程的代码(比如:验证、事务管理、缓存、日志记录等), 横向抽取出来放在一个公共的类中, 也就是切面.可以通过Spring框架来配置该切面

weixin_48656058的博客 3344

2025年最新自动化/控制保研夏令营&预推免面试真题分享(东南大学苏州校区/华东理工/南航/天大)

笔者来2021级本科自动化专业,以下部分将介绍我在夏令营以及预推免期间发生经历和问题。

Control Everything 2583

【学习系列】SAP RAP 8:行为定义-Authorization Control

本文介绍了SAP RAP中的授权控制实现方法,主要分为读取操作和修改操作两种场景。读取操作通过ABAP CDS的数据控制语言(DCL)实现,使用访问控制对象限制数据可见范围;修改操作则通过行为定义中的权限检查,包括全局授权、实例授权和预检查三种方式。文章详细说明了如何使用DCL创建访问控制规则,并通过实例演示了全局授权和实例授权的代码实现方法,以及预检查功能在数据更新前的权限验证。这些授权控制机制共同确保用户只能访问和操作其权限范围内的数据,有效保障系统安全性。

DeveloperMrMeng的博客 436

Spring基础3——AOP,事务管理

AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理

种一棵树最好的时间是十年前,其次是现在 4338

实验7:机器学习油田产量预测-代码数据-完整代码数据可直接运行-计算机毕设

实验7:机器学习油田产量预测-代码数据-完整代码数据可直接运行-计算机毕设

机器学习深度学习业余选手 479

Spring声明式事务管理,通过Spring AOP实现

Spring声明式事务管理,通过Spring AOP实现声明式事务管理:  引自:http://www.javaeye.com/post/343748  Spring也提供了声明式事务管理。这是通过Spring AOP实现的。  Spring中进行事务管理的通常方式是利用AOP(面向切片编程)的方式,为普通java类封装事务控制,它是通过动态代理实现的,由于接口是延迟实例化的,spring在这段时

tezhan120的专栏 3216

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Spring 事务管理的核心是通过 AOP 实现声明式事务,而掌握的关键在于

Spring事务管理是数据库操作的核心单元,通过ACID特性保证数据一致性。Spring提供声明式(@Transactional)和编程式两种事务管理方式,前者通过AOP实现无侵入式事务控制。关键点包括:事务传播行为(如REQUIRED、REQUIRES_NEW)、隔离级别(如READ_COMMITTED)以及事务失效场景(如自调用、异常捕获等)。最佳实践包括缩小事务范围、设置只读查询、避免长事务等。理解这些机制能有效避免生产环境中的事务问题。

从零开始,掌握网络安全。提供全面的网络安全教程、攻防演练技巧和行业动态。 1016

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Spring及Spring boot 第四章-第二节 Spring声明式事务管理 @Transaction AOP实现

Springboot AOP的意义在于对某个过程的监管,我们熟知的日志技术以及这里的事务管理。 这也是声明式事务管理 aop实现过程的意义。 doService(); 加入aop operation_beforeService(); doService(); operation_afterService(); 熟悉事务你就会明白事务的特性在于保证一系列操作的整体有效性,而不是某条语句,这和aop思想不谋而合,aop思想就可以通过将一系列操作织入到具体的流程当中来保证整体的有效性。 二者的意义具有共同

qq_44587855的博客 518

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

【Spring】—AOP之AspectJ注解方式实现声明式事务管理

前言 这回来说下注解方式的声明式事务管理。 正文 Demo 1、引入相关的jar包这里写图片描述 2、引入AOP约束<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www....

weixin_33728268的博客 291

Spring学习--通过AOP的方式实现声明式事务管理

写在前面 在Spring整合Myabatis

theLuckyLong的博客 298

基于Spring AOP实现的声明式事务管理的知识点归纳

基于Spring AOP实现的声明式事务管理的知识点归纳 Spring 事务管理机制的实现原理 Spring学习之Spring AOP的底层实现技术

rogerlin0330的博客 319

Spring的事务管理:二类:声明式事务管理(通过配置实现)---AOP

搭建Spring的事务管理的环境(转账测试) 其中jar包 结构目录为: 创建Service的接口和实现类 创建DAO的接口和实现类 配置Service和DAO:交给Spring管理 在DAO中编写扣钱和加钱方法: 配置连接池和JDBC的模板 在DAO注入Jdbc的模板: Spring的事务管理:二类:声明式事务管理(通过配置实现)—AOP 一: XML方式的声明...

kior的博客 291

Spring的事务管理 - 声明式事务

基于Spring AOP实现。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。编程式事务管理使用可实现更细粒度的事务控制。声明式事务管理不需要入侵代码,通过就可以进行事务操作,更快捷而且简单且大部分业务都可以满足,推荐使用。其实不管是编程式事务还是声明式事务,最终调用的底层核心代码是一致的。

埃泽漫笔 1556

Spring:AOP面向切面编程:代理模式实现共性功能+声明式事务管理

Spring相关概念之AOP 和其工作流程,和案例编写 Spring的声明式事务管理

habe33的博客 780

Spring之AOP

有没有可能不编写实现类,直接在运行期创建某个interface的实例呢? 这是可能的,因为Java标准库提供了一种动态代理(Dynamic Proxy)的机制:可以在运行期动态创建某个interface的实例。 什么叫运行期动态创建?听起来好像很复杂。所谓动态代理,是和静态相对应的。我们来看静态代码怎么写: 还有一种方式是动态代码,我们仍然先定义了接口Hello,但是我们并不去编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()创建了一个Hello接口对象。这种没有实现

qq_45382625的博客 363

Spring 声明式事务:从原理到实现的完整解析

声明式事务通过 "接口抽象 + 模板方法 + AOP+ThreadLocal" 的组合设计,实现了事务管理与业务逻辑的解耦。事务管理器定义事务操作规范,等实现类适配具体数据源。传播行为:控制事务嵌套关系,解决 "事务方法调用事务方法" 的场景。同步管理:通过和 ThreadLocal,保证线程内资源唯一性。AOP 实现拦截注解,织入事务逻辑。隔离级别:平衡并发性能和数据一致性,解决脏读、不可重复读等问题。

Landcc的博客 1596
上一篇: Java 并发编程中的常见问题(死锁、竞态条件等)与 JMM 有什么关系?
下一篇: 为什么需要 AOP?
冰糖心书房
博客等级 码龄15年 6016粉丝 690原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰糖心书房

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值