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

75 阅读19分钟

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

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

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

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

2001914.jpg

来看下今天的知识点:

  • 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
@Service
public class UserService {
    public String getUserById(Long id) {
        System.out.println("目标方法:查询用户信息,id=" + id);
        return "用户" + id; // 正常返回,若抛异常可测试@AfterThrowing
    }
}

切面类(包含5种通知)

@Aspect
@Component
public 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()获取代理对象,用代理对象调用方法
@Service
public class UserService {
    public void methodA() {
        // 用代理对象调用methodB
        UserService proxy = (UserServiceAopContext.currentProxy();
        proxy.methodB();
    }
    @Log // AOP增强注解
    public void methodB() { /* 业务逻辑 */ }
}
依赖注入自身

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

拆分类

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

静态方法 / 私有方法失效

底层原因

  • 静态方法无法增强

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

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

解决方案

  • 避免在静态方法 / 私有方法中写需要 AOP增强的逻辑;
  • 将静态方法改为实例方法,私有方法改为 public/protected 方法。

Spring 事务

事务传播机制

Spring 定义了 7 种传播机制

传播机制事务要求异常影响范围适用场景
REQUIRED有则加入,无则新建整个事务回滚核心业务(下单 + 扣库存)
REQUIRES_NEW强制新建独立事务仅自身回滚,不影响主事务日志记录、消息发送
SUPPORTS可有可无无事务则无影响普通查询
NOT_SUPPORTED强制非事务无事务,无回滚纯查询、缓存操作
MANDATORY必须有事务无事务抛异常核心数据修改
NEVER必须无事务有事务抛异常日志归档、非事务操作
NESTED嵌套在主事务中(保存点)仅自身回滚,主事务可继续批量操作(部分失败)

REQUIRED

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

  • 定义:若当前存在事务,则加入当前事务;若当前无事务,则新建事务。
  • 特点:所有被REQUIRED修饰的方法共用一个事务任意方法异常会导致整个事务回滚。

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

// 订单服务(主事务Bean)
@Service
public 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)
@Service
public 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)
@Service
public 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)
@Service
public class LogService {
    @Autowired
    private LogMapper logMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void recordLog(String content) {
        logMapper.insertLog(content);
    }
}

SUPPORTS

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

  • 定义:若当前存在事务,则加入;若当前无事务,则以非事务方式执行。
  • 特点:可有可无 的事务支持,适用于查询类方法

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

// 非事务环境调用SUPPORTS方法
@Service
public class NonTxService {
    @Autowired
    private LogQueryService logQueryService; // 跨Bean调用

    // 无事务方法调用SUPPORTS
    public void testSupportsWithoutTx() {
        logQueryService.log("非事务日志");
        // 模拟异常:尝试回滚(但SUPPORTS是非事务执行,无法回滚)
        int i = 1 / 0;
    }
}

// 事务环境调用SUPPORTS方法
@Service
public 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 场景:查询订单统计,强制非事务执行,即使调用方有事务

@Service
public 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:日志已插入");
    }
}

@Service
public 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 场景:订单支付 必须在事务中执行,否则报错

@Service
public class PayService {
    @Autowired
    private PayMapper payMapper;

    @Transactional(propagation = Propagation.MANDATORY)
    public void payOrder(String orderNo, BigDecimal amount) {
        payMapper.insertPayRecord(orderNo, amount);
    }
}

// 测试类
@SpringBootTest
public 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相反,强制禁止事务,适用于绝对不能在事务中执行的操作
@Service
public class ArchiveService {
    @Transactional(propagation = Propagation.NEVER)
    public void archiveLog() {
        // 日志归档逻辑
    }
}

// 测试:事务中调用NEVER方法
@SpringBootTest
public class TransactionTest {
    @Autowired
    private ArchiveService archiveService;

    @Test(expected = IllegalTransactionStateException.class)
    @Transactional
    public void testNeverWithTx() {
        archiveService.archiveLog(); // 抛异常
    }
}

NESTED

嵌套事务,依赖主事务

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

  • 特点

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

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

// 批量订单服务(主事务Bean)
@Service
public 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)
@Service
public 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

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

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

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

方法触发时机关键注意事项
suspend()事务被挂起时 比如REQUIRES_NEW 传播机制下,外层事务被挂起暂存事务相关资源
resume()事务被恢复时(挂起的事务重新激活)恢复暂存的资源
beforeCommit(boolean readOnly)事务提交前commit()执行前可做最终数据校验,readOnly 表示是否只读事务
beforeCompletion()事务完成前 无论提交 / 回滚 在beforeCommit之后 commit/rollback之前适合做资源清理(如关闭流)
afterCommit()事务成功提交后仅在事务提交成功后触发,可执行异步操作(如发消息)
afterCompletion(int status)事务最终完成后(提交 / 回滚都触发)区分事务状态STATUS_COMMITTED STATUS_ROLLED_BACK

使用场景有哪些?

  • 事务提交后发送 MQ 消息、更新缓存;
  • 事务回滚后清理临时文件 / 数据;
  • 事务提交前做数据一致性校验;
  • 跨数据源事务的同步(如 MySQL+Redis 的一致性保证)

demo

@Service
public 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、事务状态),确保多线程下事务隔离。
  • 异常回滚规则:默认仅回滚RuntimeExceptionError,需通过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 与事务底层,从会用到懂原理~~

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