写点什么

ava 王者修炼手册【Spring 篇 - AOP 与事务】:底层原理 + 实战避坑全攻略

作者:DonaldCen
  • 2025-12-05
    广东
  • 本文字数:12271 字

    阅读完需:约 40 分钟

ava 王者修炼手册【Spring 篇 - AOP 与事务】:底层原理 + 实战避坑全攻略

大家好,我是程序员强子。

工作中使用 Spring AOP 与事务时,

切面失效事务不回滚方法内调用无法触发增强等问题屡见不鲜

今天我们就剖析这些高频问题的根源~

来看下今天的知识点:

  • AOP 核心基础概念以及底层原理:基础概念,5 种通知类型及执行顺序,切点表达式底层流程 AOP 失效场景

  • Spring 事务管理核心:7 种事务传播机制(REQUIRED、REQUIRES_NEW、SUPPORTS、NESTED 等);事务核心组件:PlatformTransactionManager;TransactionDefinition;TransactionStatus;TransactionSynchronizationManager;声明式事务(@Transactional)底层执行流程

AOP 核心

连接点(JoinPoint)

本质是程序执行过程中可被切面拦截的位置

在 Spring AOP 里,因为只支持方法级别的增强

所以连接点特指 目标对象的每个方法执行时的那个瞬间 / 位置

在代码里,比如 UserService 有 addUser()、getUser()、deleteUser()三个方法

每个方法执行的那一刻(比如调用 addUser("张三")时),就是一个独立的连接点

简单说:连接点是 所有能被切面盯上的具体位置,是候选对象

切点(Pointcut)

定义

是匹配连接点的 规则 / **条件 ,是筛选器

作用是从所有连接点里,挑出真正想增强的那些连接点

比如规则 execution(* com.example.service.UserService.add*(..))就是切点

UserService 中 有三个候选的连接点: addUser()、getUser()、deleteUser()

最终选中以 add 开头的方法 addUser()

高频切点表达式

execution:方法精准匹配(最常用)

案例

// 匹配com.xxx.service包下所有类的所有公共方法@Pointcut("execution(public * com.xxx.service.*.*(..))")// 匹配com.xxx.service及其子包下所有类的所有方法@Pointcut("execution(* com.xxx.service..*.*(..))")
复制代码
  • *:匹配任意字符(如返回值、方法名、类名)

  • ..:匹配任意层级的包或任意个数 / 类型的参数

@annotation:注解匹配(灵活度高)

demo: 自定义 @Log 注解

// 1. 自定义注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Log {}
// 2. 切点表达式@Pointcut("@annotation(com.xxx.annotation.Log)")public void logAnnotationPointcut() {}
复制代码

@annotation(注解全类名),匹配所有标注了该注解的方法

within:类 / 包匹配(粗粒度)

// 匹配UserService类的所有方法@Pointcut("within(com.xxx.service.UserService)")// 匹配com.xxx.service包下所有类的方法@Pointcut("within(com.xxx.service.*)")
复制代码

within(包名.类名),匹配指定类或包所有类的方法

通知(Advice)

具体动作 ,是增强逻辑

类型

在切点匹配的连接点上执行的增强逻辑,分 5 种类型:

  • 前置(@Before)

  • 后置(@After)

  • 返回后(@AfterReturning)

  • 异常后(@AfterThrowing)

  • 环绕(@Around)

正常执行流程

@Around(前) → @Before → 目标方法 → @Around(后) → @AfterReturning → @After

举个具体的例子

目标 Service

// 目标Service@Servicepublic class UserService {    public String getUserById(Long id) {        System.out.println("目标方法:查询用户信息,id=" + id);        return "用户" + id; // 正常返回,若抛异常可测试@AfterThrowing    }}
复制代码

切面类(包含 5 种通知)

@Aspect@Componentpublic class LogAspect {    // 定义切点:匹配UserService的所有方法    @Pointcut("execution(* com.xxx.service.UserService.*(..))")    public void userServicePointcut() {}
    @Before("userServicePointcut()")    public void logBefore(JoinPoint joinPoint) {        System.out.println("【@Before】目标方法执行前,方法名:" + joinPoint.getSignature().getName());    }
    @After("userServicePointcut()")    public void logAfter(JoinPoint joinPoint) {        System.out.println("【@After】目标方法执行后,方法名:" + joinPoint.getSignature().getName());    }
    @AfterReturning(value = "userServicePointcut()", returning = "result")    public void logAfterReturning(JoinPoint joinPoint, Object result) {        System.out.println("【@AfterReturning】目标方法正常返回,返回值:" + result);    }
    @AfterThrowing(value = "userServicePointcut()", throwing = "e")    public void logAfterThrowing(JoinPoint joinPoint, Exception e) {        System.out.println("【@AfterThrowing】目标方法抛出异常,异常信息:" + e.getMessage());    }
    @Around("userServicePointcut()")    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {        System.out.println("【@Around】目标方法执行前增强");        // 执行目标方法(必须调用,否则目标方法不执行)        Object result = joinPoint.proceed();        System.out.println("【@Around】目标方法执行后增强");        return result;    }}
复制代码

最终执行顺序(正常返回场景)

【@Around】目标方法执行前增强【@Before】目标方法执行前,方法名:getUserById目标方法:查询用户信息,id=1【@Around】目标方法执行后增强【@After】目标方法执行后,方法名:getUserById【@AfterReturning】目标方法正常返回,返回值:用户1
复制代码

注意:**@Around 是环绕通知, 而 @Around(前)和 @Around(后)的分界点,就是 ProceedingJoinPoint**.proceed()方法 ,所以是有 前后之分

举个例子

@Around("userServicePointcut()")public Object around(ProceedingJoinPoint pjp) throws Throwable {    // ----------------- @Around(前)的逻辑 -----------------    System.out.println("[Around前] 目标方法执行前的增强(比如参数校验、日志)");    Object[] args = pjp.getArgs(); // 获取目标方法参数    if (args == null || args.length == 0) {        throw new IllegalArgumentException("参数不能为空"); // 可阻止目标方法执行    }
    // ----------------- 执行目标方法 -----------------    Object result = pjp.proceed(); // 调用目标方法(比如UserService.addUser())
    // ----------------- @Around(后)的逻辑 -----------------    System.out.println("[Around后] 目标方法执行后的增强(比如处理返回值、记录耗时)");    return result + "_增强后的返回值"; // 可修改目标方法返回值}
复制代码

@Around(前):proceed()之前的 参数校验前置日志

@Around(后):proceed()之后的 修改返回值后置日志

异常执行流程

@Around(前) → @Before → 目标方法(抛异常) → @AfterThrowing → @After(@Around(后)逻辑不会执行,除非捕获异常)

切面(Aspect)

切点 + 通知的组合

封装横切逻辑的一个单独的类,命名的习惯是:xxxAspect, 比如 LogAspect 类

织入(Weaving)

切面逻辑植入目标对象的过程

Spring AOP 通过运行时动态代理(JDK/CGLIB)实现织入

动态代理 原理介绍 在上一篇文章,欢迎查看

为什么动态代理要生成一个代理对象?

不修改目标对象代码,实现无侵入增强

租房中介就是 代理对象:

  • 中介会先帮你核实房源(前置增强);

  • 再带你看房(调用目标对象的 租房 方法);

  • 最后帮你办合同(后置增强

代理生成的代理类,会保存到磁盘吗?为啥看不到?

默认情况下只存在于内存中不会保存到磁盘

生成后直接加载到 JVM 中使用进程结束后就会被回收

AOP 底层流程

Spring AOP 的底层依赖动态代理Bean 后置处理器实现

核心流程可拆解为:

  • 切面扫描解析

  • 代理 Bean 创建

  • 通知链执行

切面的扫描与解析

Spring AOP 的切面扫描核心类:AnnotationAwareAspectJAutoProxyCreator

是一个 BeanPostProcessorBean 后置处理器,核心步骤:

  • 扫描切面:在 Bean 初始化过程中,扫描容器中所有标注 @Aspect 注解的类

  • 解析切面:将切面中的 @Pointcut、@Before 等注解解析为 Spring 内部的 Advisor 对象 Advisor=切点+通知,AOP 的最小执行单元

  • 执行:AnnotationAwareAspectJAutoProxyCreator 通过 findCandidateAdvisors()方法获取所有切面的 Advisor,再通过 sortAdvisors()按优先级排序

代理 Bean 的创建

根据切点条件 ,查找合适的目标 Bean(之前扫描处理的 bean)

接下来不操作 目标 Bean,而是通过 createProxy()方法创建代理 Bean

  • 获取当前 Bean 的所有匹配的 Advisor

  • 确定代理类型(JDK/CGLIB)

  • 通过 ProxyFactory 创建代理对象并返回

通知的执行链

调用代理 Bean 的目标方法时,不会直接执行目标方法,而是触发通知执行链

核心由 ReflectiveMethodInvocation 类的 proceed()方法实现

执行逻辑拆解:

  • 调用 proceed()方法时,依次执行所有前置通知

  • 当所有前置通知执行完毕,调用 joinPoint.proceed()执行目标方法

  • 目标方法执行后,依次执行后置通知返回通知 / 异常通知

AOP 失效场景

自调用问题

问题现象是怎么样的?

Service 类中方法 A -> 本类方法 B

若方法 B 被 AOP 增强,则增强逻辑不执行

底层原因

AOP 的增强逻辑是通过代理 Bean 触发的

而内部方法调用时,使用的是 this 关键字,而非代理对象,因此无法触发通知执行链

解决方案

暴露代理

  • 开启暴露代理:在启动类添加 @EnableAspectJAutoProxy(exposeProxy = true);

  • 通过 AopContext.currentProxy()获取代理对象,用代理对象调用方法

@Servicepublic class UserService {    public void methodA() {        // 用代理对象调用methodB        UserService proxy = (UserService) AopContext.currentProxy();        proxy.methodB();    }    @Log // AOP增强注解    public void methodB() { /* 业务逻辑 */ }}
复制代码

依赖注入自身

通过 @Autowired 将自身注入到当前类

拆分类

将方法 B 抽取到新的 Service 类中,通过依赖注入调用,避免内部调用

静态方法 / 私有方法失效

底层原因

  • 静态方法无法增强 JDK 代理:基于接口,静态方法属于类,接口中无法定义静态方法,因此无法代理;CGLIB 代理:基于继承,静态方法是类级别的方法,子类无法重写父类的静态方法,因此无法通过字节码增强植入通知。

  • 私有方法无法增强 CGLIB 代理的子类无法访问父类的私有方法,更无法重写;JDK 代理同样无法代理私有方法(接口无私有方法),因此私有方法的 AOP 增强完全不生效

解决方案

  • 避免在静态方法 / 私有方法中写需要 AOP 增强的逻辑;

  • 将静态方法改为实例方法,私有方法改为 public/protected 方法。

Spring 事务

事务传播机制

Spring 定义了 7 种传播机制

REQUIRED

默认,支持当前事务,无则新建

  • 定义:若当前存在事务,则加入当前事务;若当前无事务,则新建事务。

  • 特点:所有被 REQUIRED 修饰的方法共用一个事务任意方法异常会导致整个事务回滚。

Demo :下单时扣减库存,两者同属一个事务,保证失败同时回滚

// 订单服务(主事务Bean)@Servicepublic class OrderService {    @Autowired    private OrderMapper orderMapper;    @Autowired    private StockService stockService; // 库存服务(另一个Bean)
    @Transactional(propagation = Propagation.REQUIRED)    public void createOrder(String orderNo, String productId, int count) {        // 1. 本Bean内操作(主事务)        orderMapper.insertOrder(orderNo, productId, count);        // 2. 调用另一个Bean的事务方法(加入主事务)        stockService.deductStock(productId, count);                // 模拟异常:整体回滚(订单和库存都回滚)        // int i = 1 / 0;    }}

// 库存服务(独立Bean)@Servicepublic class StockService {    @Autowired    private StockMapper stockMapper;
    @Transactional(propagation = Propagation.REQUIRED)    public void deductStock(String productId, int count) {        stockMapper.deductStock(productId, count);    }}
复制代码

REQUIRES_NEW

新建事务,挂起当前事务

  • 定义:无论当前是否有事务,都新建独立事务;若当前有事务,则先挂起当前事务,待新事务完成后恢复。

  • 特点:新事务与原事务相互独立,异常仅影响自身,不影响原事务

Demo :下单时记录日志日志事务独立,即使下单失败,日志仍保存 ,新事物独立,不被原事务影响~

// 订单服务(主事务Bean)@Servicepublic class OrderService {    @Autowired    private OrderMapper orderMapper;    @Autowired    private LogService logService; // 日志服务(独立Bean)
    @Transactional(propagation = Propagation.REQUIRED)    public void createOrder(String orderNo) {        orderMapper.insertOrder(orderNo, "P001", 1);        // 调用另一个Bean的REQUIRES_NEW方法(新建独立事务)        logService.recordLog("创建订单:" + orderNo);                // 模拟主事务异常:订单回滚,日志不回滚(新事务已提交)        int i = 1 / 0;    }}
// 日志服务(独立Bean)@Servicepublic class LogService {    @Autowired    private LogMapper logMapper;
    @Transactional(propagation = Propagation.REQUIRES_NEW)    public void recordLog(String content) {        logMapper.insertLog(content);    }}
复制代码

SUPPORTS

支持当前事务,无则非事务执行

  • 定义:若当前存在事务,则加入;若当前无事务,则以非事务方式执行。

  • 特点:可有可无 的事务支持,适用于查询类方法

Demo :查询订单详情,若在事务中则加入,否则非事务执行

// 非事务环境调用SUPPORTS方法@Servicepublic class NonTxService {    @Autowired    private LogQueryService logQueryService; // 跨Bean调用
    // 无事务方法调用SUPPORTS    public void testSupportsWithoutTx() {        logQueryService.log("非事务日志");        // 模拟异常:尝试回滚(但SUPPORTS是非事务执行,无法回滚)        int i = 1 / 0;    }}
// 事务环境调用SUPPORTS方法@Servicepublic class TxService {    @Autowired    private LogQueryService logQueryService; // 跨Bean调用
    // 事务方法调用SUPPORTS    @Transactional(propagation = Propagation.REQUIRED)    public void testSupportsWithTx() {        logQueryService.log("事务日志"); // SUPPORTS加入当前事务        // 模拟异常:触发事务回滚        int i = 1 / 0;    }}
复制代码

NOT_SUPPORTED

不支持事务,挂起当前事务

  • 定义:以非事务方式执行;若当前有事务,则先挂起当前事务,执行完后恢复。

  • 特点:强制非事务执行,适用于无需事务的操作(如纯查询、缓存更新)

Demo 场景:查询订单统计,强制非事务执行,即使调用方有事务

@Servicepublic class LogService { // 独立Bean,提供NOT_SUPPORTED方法    @Autowired    private LogMapper logMapper;
    @Transactional(propagation = Propagation.NOT_SUPPORTED)    public void recordLog(String content) {        logMapper.insertLog(content); // 非事务插入日志        System.out.println("NOT_SUPPORTED:日志已插入");    }}
@Servicepublic class OrderService {    @Autowired    private LogMapper logMapper;    @Autowired    private LogService logService; // 跨Bean调用NOT_SUPPORTED方法
    @Transactional(propagation = Propagation.REQUIRED)    public void createOrder(String orderNo) {        // 1. 外层事务操作:插入订单(受事务控制)        logMapper.insertOrder(orderNo);        System.out.println("外层事务:订单已插入");
        // 2. 调用NOT_SUPPORTED方法(强制非事务,挂起当前事务)        logService.recordLog("订单创建日志:" + orderNo);
        // 3. 模拟外层事务异常:触发订单回滚,但日志不会回滚        int i = 1 / 0;    }}
复制代码

MANDATORY

必须在事务中执行,否则抛异常

  • 定义:必须在现有事务中执行;若当前无事务,则直接抛出 IllegalTransactionStateException。

  • 特点:强制依赖上级事务,适用于 必须在事务中完成 的操作,比如核心数据修改

Demo 场景:订单支付 必须在事务中执行,否则报错

@Servicepublic class PayService {    @Autowired    private PayMapper payMapper;
    @Transactional(propagation = Propagation.MANDATORY)    public void payOrder(String orderNo, BigDecimal amount) {        payMapper.insertPayRecord(orderNo, amount);    }}
// 测试类@SpringBootTestpublic class TransactionTest {    @Autowired    private PayService payService;
    // 非事务调用MANDATORY方法:抛异常    @Test(expected = IllegalTransactionStateException.class)    public void testMandatoryWithoutTx() {        payService.payOrder("O001", new BigDecimal("100"));    }
    // 事务中调用:正常执行    @Test    @Transactional    public void testMandatoryWithTx() {        payService.payOrder("O001", new BigDecimal("100"));    }}
复制代码

NEVER

必须非事务执行,否则抛异常

  • 定义:必须以非事务方式执行;若当前有事务,则抛出 IllegalTransactionStateException。

  • 特点:与 MANDATORY 相反,强制禁止事务,适用于绝对不能在事务中执行的操作

@Servicepublic class ArchiveService {    @Transactional(propagation = Propagation.NEVER)    public void archiveLog() {        // 日志归档逻辑    }}
// 测试:事务中调用NEVER方法@SpringBootTestpublic class TransactionTest {    @Autowired    private ArchiveService archiveService;
    @Test(expected = IllegalTransactionStateException.class)    @Transactional    public void testNeverWithTx() {        archiveService.archiveLog(); // 抛异常    }}
复制代码

NESTED

嵌套事务,依赖主事务

  • 定义:若当前有事务,则在当前事务内创建嵌套事务(保存点机制);若当前无事务,则新建事务

  • 特点嵌套事务是主事务的子事务,主事务回滚则嵌套事务必回滚;嵌套事务回滚不影响主事务(仅回滚到保存点);

Demo 场景:批量下单,其中一个订单失败仅回滚该订单,不影响其他订单

// 批量订单服务(主事务Bean)@Servicepublic class BatchOrderService {    @Autowired    private SingleOrderService singleOrderService; // 单个订单服务(独立Bean)
    @Transactional(propagation = Propagation.REQUIRED)    public void batchCreateOrder(List<String> orderNos) {        for (String orderNo : orderNos) {            try {                // 调用另一个Bean的NESTED方法(嵌套事务)                singleOrderService.createSingleOrder(orderNo);            } catch (Exception e) {                // 嵌套事务回滚,主事务继续                System.err.println("订单" + orderNo + "失败:" + e.getMessage());            }        }    }}
// 单个订单服务(独立Bean)@Servicepublic class SingleOrderService {    @Autowired    private OrderMapper orderMapper;
    @Transactional(propagation = Propagation.NESTED)    public void createSingleOrder(String orderNo) {        orderMapper.insertOrder(orderNo, "P001", 1);        if ("O002".equals(orderNo)) {            throw new RuntimeException("库存不足"); // 嵌套事务回滚        }    }}
复制代码

事务核心组件

TransactionDefinition

事务的 规则定义 ,定义事务的属性规则

  • 作用传播机制:如 REQUIRED、REQUIRES_NEW(决定事务如何传播);隔离级别:如 READ_COMMITTED(解决脏读、不可重复读等问题);超时时间:事务最长执行时间(超时自动回滚);只读属性:标记事务是否为只读

  • 默认实现 DefaultTransactionDefinition 提供默认值,如传播机制默认 REQUIRED,隔离级别 默认 数据库默认

TransactionAttribute

TransactionAttribute 继承自 TransactionDefinition

并新增了与注解事务相关的扩展方法(主要是异常回滚规则)

专门适配 @Transactional 注解的解析场景

TransactionAttribute 新增的关键方法

// 判断给定异常是否触发事务回滚(适配@Transactional的rollbackFor/noRollbackFor)boolean rollbackOn(Throwable ex);// 获取事务的描述(如方法名,用于日志)String getName();
复制代码

这些方法是为了支持 @Transactional 注解中的 rollbackFornoRollbackFor 等属性

TransactionStatus

跟踪事务的实时状态,是事务的 状态记录本

作用是什么?

保存事务执行过程中的状态信息,供 PlatformTransactionManager 判断如何操作事务(提交 / 回滚)

有哪些核心属性 / 方法?

  • isNewTransaction():是否是新建事务(区分 加入现有事务新建事务);

  • setRollbackOnly():标记事务必须回滚(即使无异常);

  • isRollbackOnly():判断事务是否需要回滚

  • hasSavepoint():是否存在保存点(用于 NESTED 嵌套事务

PlatformTransactionManager

事务的 执行者 ,Spring 事务管理的核心接口 ,事务的总指挥

定义了事务的核心操作(获取事务、提交、回滚)

作用是什么?

根据 TransactionDefinition 的规则,完成事务的生命周期管理(开启、提交、回滚),不同数据源 / 持久化框架有不同实现:

  • DataSourceTransactionManager:适配 JDBC/MyBatis(基于 java.sql.Connection);

  • JpaTransactionManager:适配 JPA(基于 JPA 的 EntityManager);

  • HibernateTransactionManager:适配 Hibernate(基于 Session)

核心方法是什么?

// 根据事务定义,获取/创建事务,返回事务状态TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 提交事务(根据事务状态判断是否真提交)void commit(TransactionStatus status) throws TransactionException;
// 回滚事务(根据事务状态判断是否真回滚)void rollback(TransactionStatus status) throws TransactionException;
复制代码

TransactionSynchronizationManager

事务的 上下文容器 , 基于 ThreadLocal 的工具类,是事务的 线程本地仓库

作用是什么?

当前线程中存储事务相关的上下文信息(如数据库连接、事务状态、同步回调),保证多线程下事务的隔离性

核心存储内容有哪些?

  • 事务的 Connection(或 EntityManager/Session):确保同一线程内的数据库操作使用同一个连接

  • 事务状态(是否激活事务、是否只读);

  • 事务同步回调(TransactionSynchronization):事务完成后执行的钩子(如资源清理)

核心方法有哪些?

// 绑定当前线程的数据库连接static void bindResource(Object key, Object value);
// 获取当前线程绑定的数据库连接static Object getResource(Object key);
// 解绑当前线程的数据库连接static void unbindResource(Object key);
// 判断当前线程是否存在活跃事务static boolean isActualTransactionActive();
复制代码

TransactionSynchronization

事务同步回调接口,允许在事务的不同生命周期阶段(提交前、提交后、回滚后等)**插入自定义逻辑 **

相当于给事务加了 钩子函数,让我们能在事务关键节点执行额外操作 ,比如提交后发送消息、回滚后清理资源

有哪些核心方法及触发时机?

使用场景有哪些?

  • 事务提交后发送 MQ 消息、更新缓存;

  • 事务回滚后清理临时文件 / 数据;

  • 事务提交前做数据一致性校验;

  • 跨数据源事务的同步(如 MySQL+Redis 的一致性保证)

demo

@Servicepublic class OrderService {    @Autowired    private OrderMapper orderMapper;    @Autowired    private MqProducer mqProducer; // 模拟MQ生产者
    @Transactional    public void createOrder(String orderNo) {        // 1. 执行事务操作:插入订单        orderMapper.insertOrder(orderNo);
        // 2. 注册事务同步器(仅在事务提交后发送MQ)        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {            @Override            public void afterCommit() {                // 事务提交后执行:发送订单创建成功消息                mqProducer.send("order_topic", "订单" + orderNo + "已创建");                System.out.println("事务提交后发送MQ:" + orderNo);            }
            @Override            public void afterCompletion(int status) {                // 事务完成后执行(提交/回滚都触发)                if (status == STATUS_ROLLED_BACK) {                    System.out.println("事务回滚:清理订单" + orderNo + "的临时数据");                }            }        });
        // 模拟异常:若抛出异常,事务回滚,afterCommit不会执行        // int i = 1 / 0;    }}
复制代码
  • 无异常:事务提交,afterCommit()触发,MQ 消息发送成功;

  • 有异常:事务回滚,afterCommit()不触发,afterCompletion()检测到 STATUS_ROLLED_BACK,执行清理逻辑。

简化写法

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {    @Override    public void afterCommit() {        mqProducer.send("order_topic", "订单" + orderNo + "已创建");    }});
复制代码

@Transactional 底层执行流程

启动时

  • 扫描注解AnnotationTransactionAttributeSource 会扫描 Bean 中标记 @Transactional 的方法 / 类,解析注解属性(传播机制、隔离级别、超时时间等),封装为 TransactionAttribute (继承自 TransactionDefinition)

  • 创建事务切面:Spring 将 TransactionInterceptor(通知)与 TransactionAttributeSource(切点规则)组合成 BeanFactoryTransactionAttributeSourceAdvisor(事务切面),注册到容器中。

Bean 初始化

当容器初始化带有 @Transactional 的 Bean

  • AnnotationAwareAspectJAutoProxyCreator(AOP 自动代理创建器)检测到该 Bean 匹配事务切面的切点;

  • 根据 Bean 类型选择代理方式(JDK 动态代理 / CGLIB),生成事务代理对象

  • 代理对象的方法调用会被 TransactionInterceptor 拦截。

方法调用时

调用代理对象的事务方法时,TransactionInterceptor 的 invoke()方法会执行以下步骤:

  • 获取事务属性:从 TransactionAttributeSource 中获取当前方法的 @Transactional 配置(如传播机制、隔离级别)

  • 获取事务管理器:根据数据源类型匹配对应的 PlatformTransactionManager(如 DataSourceTransactionManager

  • 开启 / 加入事务调用 PlatformTransactionManager.getTransaction(...),根据传播机制决定即根据事务传播机制处理

  • 执行目标方法:调用目标对象的真实方法

  • 事务提交 / 回滚若方法正常返回:调用 PlatformTransactionManager.commit()提交事务若方法抛出异常:根据 rollbackFor 判断是否回滚,是则调用 rollback(),否则提交

核心细节

  • 事务与线程绑定:TransactionSynchronizationManager 通过 ThreadLocal 存储当前线程的事务信息(如 Connection、事务状态),确保多线程下事务隔离。

  • 异常回滚规则:默认仅回滚 RuntimeException Error,需通过 rollbackFor 指定检查型异常(如 @Transactional(rollbackFor = Exception.class))。

  • 代理对象的必要性:若直接调用目标对象的方法(非代理),会绕过 TransactionInterceptor,事务失效(即 自调用问题)。

总结

今天把 Spring AOP 与事务的核心底层逻辑给学扎实了!

本文聚焦 Spring AOP 与事务四大核心实战内容:

  1. AOP 五大核心基础概念,@Before 等 5 种通知类型的执行顺序,以及 execution、@annotation、within 三种切点表达式的核心用法;

  2. Spring AOP 的完整底层流程(切面扫描解析、代理 Bean 创建、通知执行链调用),以及 AOP 失效的典型场景;

  3. 事务 7 种传播机制(含 REQUIRED、REQUIRES_NEW、SUPPORTS、NESTED 等核心场景),及 PlatformTransactionManager、TransactionDefinition 等四大事务核心组件的作用;

  4. 声明式事务 @Transactional 注解的底层执行流程,从注解解析到事务开启、提交 / 回滚的全链路逻辑。

帮 我们 吃透 Spring AOP 与事务底层,从会用到懂原理~~

熟练度刷不停,知识点吃透稳,下期接着练~

发布于: 刚刚阅读数: 3
用户头像

DonaldCen

关注

有个性,没签名 2019-01-13 加入

跟我在峡谷学Java 公众号:程序员悟空的宝藏乐园

评论

发布
暂无评论
ava 王者修炼手册【Spring 篇 - AOP 与事务】:底层原理 + 实战避坑全攻略_AOP 核心_DonaldCen_InfoQ写作社区