【Spring】AOP深度解析:代理机制、拦截器链与事务失效全解

Spring AOP深度解析:代理机制、拦截器链与事务失效全解

一、JDK动态代理 vs CGLIB:代理机制的本质差异

Spring AOP通过代理模式实现横切逻辑织入,提供两种实现:JDK动态代理 (基于接口)和CGLIB(基于继承)。


1.1 JDK动态代理:接口为王

核心机制 :利用java.lang.reflect.Proxy在运行时创建接口的代理实现类。

java 复制代码
// 目标接口
public interface UserService {
    void saveUser(User user);
}

// 目标实现类
@Service
public class UserServiceImpl implements UserService {
    @Override
    public void saveUser(User user) {
        // 业务逻辑
    }
}

// JDK代理创建过程(Spring自动完成)
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 前置增强
            System.out.println("Before: " + method.getName());
            
            // 调用目标方法
            Object result = method.invoke(target, args);
            
            // 后置增强
            System.out.println("After: " + method.getName());
            return result;
        }
    }
);

// 方法调用
proxy.saveUser(new User()); // 实际执行InvocationHandler.invoke()

内存结构

复制代码
┌──────────────────────────────────────┐
│  UserService代理对象                   │
├──────────────────────────────────────┤
│  - InvocationHandler                  │
│    └─ target: UserServiceImpl        │
│  - 实现UserService接口                   │
│    └─ saveUser() → InvocationHandler.invoke()
└──────────────────────────────────────┘

关键特性

  • 只能代理实现了接口的类
  • 代理对象与目标对象实现相同接口
  • 通过反射调用目标方法(method.invoke(target, args)
  • 不能代理final方法(接口方法天然非final)

1.2 CGLIB:继承的力量

核心机制 :通过字节码生成库继承目标类,重写方法实现代理。

java 复制代码
// 目标类(无需接口)
@Service
public class OrderService {
    public void createOrder(Order order) {
        // 业务逻辑
    }
}

// CGLIB代理创建(简化版)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderService.class);
enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 前置增强
        System.out.println("Before: " + method.getName());
        
        // 通过MethodProxy调用(FastClass机制,比反射快3倍)
        Object result = proxy.invokeSuper(obj, args);
        
        // 后置增强
        System.out.println("After: " + method.getName());
        return result;
    }
});

OrderService proxy = (OrderService) enhancer.create();
proxy.createOrder(new Order());

内存结构

复制代码
┌──────────────────────────────────────┐
│  OrderService$$EnhancerByCGLIB       │
├──────────────────────────────────────┤
│  - 继承自OrderService                  │
│  - 重写createOrder()方法                │
│    └─ 调用MethodInterceptor.intercept()│
│  - 持有MethodInterceptor                │
│    └─ 包含目标方法引用                   │
└──────────────────────────────────────┘

关键特性

  • 可以代理无接口的类
  • 代理对象继承目标类OrderService$$EnhancerByCGLIB
  • 使用FastClass机制(生成字节码直接调用,绕过反射)
  • 不能代理final不能代理final方法

1.3 对比总结:Spring如何选择?

选择策略DefaultAopProxyFactory):

java 复制代码
public class DefaultAopProxyFactory implements AopProxyFactory {
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // 1. 优化标志强制使用CGLIB
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config); // 接口仍用JDK
            }
            return new ObjenesisCglibAopProxy(config); // 类用CGLIB
        }
        // 2. 默认:有接口用JDK,无接口用CGLIB
        return new JdkDynamicAopProxy(config);
    }
}

配置强制使用CGLIB

java 复制代码
@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制CGLIB
public class Application { ... }

完整对比表

特性 JDK动态代理 CGLIB 性能影响
实现方式 实现接口 继承目标类 CGLIB字节码生成耗时
代理条件 必须实现接口 类不能为final -
方法限制 接口方法 非final方法 CGLIB final方法无法代理
调用机制 反射 (慢3倍) FastClass (直接调用) CGLIB高5-10%
启动速度 慢(需生成字节码) JDK代理快20ms/类
运行时性能 慢 (反射开销) (无反射) CGLIB快5-10%
内存占用 更少 稍多 (生成类) JDK少约1KB/代理
Spring Boot默认 有接口用JDK 无接口用CGLIB 自动选择最优方案

性能基准(JMH测试,10万次调用):

复制代码
JDK动态代理: ~1200 ops/ms
CGLIB:       ~1350 ops/ms (+12%)
直接调用:    ~2000 ops/ms (基准)

结论 :CGLIB略快,但JDK代理启动更快、内存更少。Spring Boot的自动选择策略是最佳实践。


二、拦截器链:责任链模式的艺术

2.1 拦截器链执行模型

当调用代理方法时,Spring AOP构建MethodInterceptor链,按顺序执行。

java 复制代码
// 拦截器链结构
public class ReflectiveMethodInvocation implements ProxyMethodInvocation {
    private final Object proxy;
    private final Object target;
    private final Method method;
    private final Object[] arguments;
    private final List<MethodInterceptor> interceptors;
    private int currentInterceptorIndex = -1;
    
    // 核心:递归调用拦截器
    @Override
    public Object proceed() throws Throwable {
        // 1. 所有拦截器执行完毕,调用目标方法
        if (this.currentInterceptorIndex == this.interceptors.size() - 1) {
            return invokeJoinpoint(); // 调用目标方法
        }
        
        // 2. 执行下一个拦截器
        this.currentInterceptorIndex++;
        MethodInterceptor interceptor = this.interceptors.get(currentInterceptorIndex);
        
        return interceptor.invoke(this); // 传入this,用于递归proceed()
    }
}

2.2 执行流程图解

场景:UserService.saveUser()有3个增强(前置日志、事务、后置日志)

复制代码
调用 proxy.saveUser(user)
    │
    ▼
┌────────────────────────────────────────────────────────────┐
│  ReflectiveMethodInvocation.proceed()                      │
│  interceptors = [LogInterceptor, TransactionInterceptor,  │
│                  LogAfterInterceptor]                      │
└────────────────────────────────────────────────────────────┘
    │
    ├─▶ currentIndex = 0
    ├─▶ LogInterceptor.invoke(this)
    │      │
    │      ├─▶ System.out.println("Before: 日志记录")
    │      │
    │      └─▶ return methodInvocation.proceed()
    │              │
    │              ├─▶ currentIndex = 1
    │              ├─▶ TransactionInterceptor.invoke(this)
    │              │      │
    │              │      ├─▶ TransactionStatus tx = txManager.getTransaction()
    │              │      │
    │              │      ├─▶ try {
    │              │      │       Object result = methodInvocation.proceed()
    │              │      │              │
    │              │      │              ├─▶ currentIndex = 2
    │              │      │              ├─▶ LogAfterInterceptor.invoke(this)
    │              │      │              │      │
    │              │      │              │      ├─▶ System.out.println("Before After")
    │              │      │              │      │
    │              │      │              │      └─▶ return methodInvocation.proceed()
    │              │      │              │              │
    │              │      │              │              ├─▶ currentIndex = 3
    │              │      │              │              ├─▶ 所有拦截器执行完毕
    │              │      │              │              └─▶ invokeJoinpoint()
    │              │      │              │                     │
    │              │      │              │                     └─▶ target.saveUser(user)
    │              │      │              │                            │
    │              │      │              │                            └─▶ 执行真实业务逻辑
    │              │      │              │
    │              │      │              └─▶ 返回结果
    │              │      │
    │              │      ├─▶ txManager.commit(tx)
    │              │      └─▶ return result
    │              │
    │              └─▶ 返回结果
    │
    ├─▶ System.out.println("After: 日志记录")
    │
    └─▶ 返回最终结果

关键特性 :拦截器链是递归结构 ,通过proceed()串联。每个拦截器可选择前置增强后置增强环绕增强


2.3 TransactionInterceptor源码剖析

事务拦截器是最复杂、最重要的拦截器。

java 复制代码
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // 1. 获取目标类和方法
        Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
        
        // 2. 调用父类方法(事务模板方法)
        return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
    }
}

// TransactionAspectSupport.invokeWithinTransaction()
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
                                         final InvocationCallback invocation) throws Throwable {
    
    // 1. 获取事务属性(@Transactional注解解析)
    TransactionAttributeSource tas = getTransactionAttributeSource();
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    
    // 2. 获取事务管理器
    final TransactionManager tm = determineTransactionManager(txAttr);
    
    // 3. 转换为PlatformTransactionManager
    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
    
    // 4. 构建事务方法标识(类名.方法名)
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    
    // 5. 创建事务信息
    TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
    
    Object retVal;
    try {
        // 6. 执行目标方法(回调proceed)
        retVal = invocation.proceedWithInvocation();
    } catch (Throwable ex) {
        // 7. 异常回滚
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
    } finally {
        // 8. 清理事务信息
        cleanupTransactionInfo(txInfo);
    }
    
    // 9. 提交事务
    commitTransactionAfterReturning(txInfo);
    return retVal;
}

事务状态管理

java 复制代码
// TransactionAspectSupport.createTransactionIfNecessary()
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
                                                       @Nullable TransactionAttribute txAttr,
                                                       final String joinpointIdentification) {
    
    // 如果没有事务属性,非事务执行
    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
        // 获取事务状态(开启事务、传播行为处理)
        TransactionStatus status = tm.getTransaction(txAttr);
        return prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
    }
}

// DataSourceTransactionManager.getTransaction()
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) 
    throws TransactionException {
    
    Object transaction = doGetTransaction(); // 获取连接持有者
    
    // 1. PROPAGATION_REQUIRED: 已存在事务则加入,否则新建
    if (isExistingTransaction(transaction)) {
        return handleExistingTransaction(definition, transaction, debugEnabled);
    }
    
    // 2. 挂起当前事务(PROPAGATION_REQUIRES_NEW)
    // 3. 创建新事务
    return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}

2.4 拦截器链顺序

默认顺序 (由@Order或Ordered接口控制):

java 复制代码
// 1. 最高优先级:事务拦截器(@Order(Ordered.LOWEST_PRECEDENCE - 1))
TransactionInterceptor

// 2. 中间:自定义拦截器
@Aspect
@Order(1)
public class LoggingAspect { ... }

// 3. 最低优先级:缓存拦截器
CacheInterceptor

执行顺序前置增强升序后置增强降序):

复制代码
前置: Log(1) → Auth(2) → Transaction(3) → 目标方法
后置: Transaction(3) → Auth(2) → Log(1) → 返回

可视化

复制代码
LogAspect     AuthAspect    TransactionAspect    Target
   │              │               │                │
   ├─▶before()    │               │                │
   │              ├─▶before()     │                │
   │              │               ├─▶before()      │
   │              │               │                ├─▶执行方法
   │              │               ├─▶after()       │
   │              ├─▶after()      │                │
   ├─▶after()     │               │                │
   │              │               │                │
   └─▶返回结果                                       

三、@Transactional失效场景:高频踩坑

3.1 场景1:同类调用(Self-invocation)

这是最常见的失效场景(占70%的生产问题)。

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    public void createUser(User user) {
        // 调用本类方法
        saveUser(user); // ❌ @Transactional失效!
    }
    
    @Transactional // 事务注解在此处
    public void saveUser(User user) {
        userRepository.save(user);
        if (user.getAge() < 0) {
            throw new RuntimeException("年龄非法");
        }
    }
}

失效原因

  • Spring AOP基于代理对象createUser()调用的是this.saveUser()(this指向目标对象 ,而非代理对象
  • 绕过代理 = 绕过拦截器链 = 事务拦截器未执行

内存模型

复制代码
UserService代理对象
   │
   ├─▶ target: UserService目标对象
   │
   └─▶ createUser() → 不经过代理
           │
           └─▶ this.saveUser() // this是目标对象,无事务
                  │
                  └─▶ 直接执行方法,无AOP增强

UserService代理对象
   │
   └─▶ saveUser() → 经过代理
           │
           ├─▶ TransactionInterceptor.invoke()
           │       │
           │       └─▶ 开启事务 → 调用目标 → 提交/回滚
           │
           └─▶ 目标.saveUser()

解决方案

方案1:注入代理自身(推荐)

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    // 注入自己的代理对象
    @Autowired
    private UserService selfProxy;
    
    public void createUser(User user) {
        selfProxy.saveUser(user); // ✅ 通过代理调用,事务生效
    }
    
    @Transactional
    public void saveUser(User user) {
        userRepository.save(user);
    }
}

方案2:ApplicationContext获取代理

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    public void createUser(User user) {
        UserService proxy = applicationContext.getBean(UserService.class);
        proxy.saveUser(user); // ✅ 从Spring容器获取代理
    }
    
    @Transactional
    public void saveUser(User user) { ... }
}

方案3:使用AopContext(不推荐)

java 复制代码
// 需启用暴露代理配置
@EnableAspectJAutoProxy(exposeProxy = true)

public void createUser(User user) {
    ((UserService) AopContext.currentProxy()).saveUser(user);
}

方案4:重构代码(根本解决)

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private UserValidator userValidator;
    
    @Transactional // 事务注解提升到入口方法
    public void createUser(User user) {
        userValidator.validate(user); // 校验逻辑分离
        saveUser(user);
    }
    
    private void saveUser(User user) { // 私有方法,不暴露事务
        userRepository.save(user);
    }
}

3.2 场景2:异常被捕获不抛出

java 复制代码
@Service
public class OrderService {
    
    @Transactional
    public void createOrder(Order order) {
        try {
            orderRepository.save(order);
            updateInventory(order); // 可能抛出异常
        } catch (Exception e) {
            log.error("创建订单失败", e);
            // ❌ 异常被吞下,事务不会回滚
        }
    }
}

失效原因TransactionInterceptor只能拦截从目标方法抛出的异常。如果异常在方法内部被捕获,拦截器认为方法正常返回,将提交事务。

源码逻辑

java 复制代码
// TransactionInterceptor.invokeWithinTransaction()
try {
    retVal = invocation.proceedWithInvocation(); // 执行目标方法
    
} catch (Throwable ex) {
    completeTransactionAfterThrowing(txInfo, ex); // 异常处理
    throw ex; // 重新抛出异常
}
commitTransactionAfterReturning(txInfo); // 正常返回则提交

解决方案

方案1:捕获后重新抛出

java 复制代码
@Transactional
public void createOrder(Order order) {
    try {
        orderRepository.save(order);
        updateInventory(order);
    } catch (InventoryException e) {
        log.error("库存更新失败", e);
        throw new RuntimeException("订单创建失败", e); // ✅ 重新抛出
    }
}

方案2:手动回滚

java 复制代码
@Transactional
public void createOrder(Order order) {
    try {
        orderRepository.save(order);
        updateInventory(order);
    } catch (InventoryException e) {
        log.error("库存更新失败", e);
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // ✅ 标记回滚
    }
}

方案3:异常传播到上层

java 复制代码
@Transactional
public void createOrder(Order order) throws InventoryException {
    orderRepository.save(order);
    updateInventory(order); // 不捕获,让异常传播
}

3.3 场景3:检查型异常不回滚

java 复制代码
@Transactional
public void transferMoney(Account from, Account to, BigDecimal amount) throws InsufficientFundsException {
    from.debit(amount); // 可能抛出检查型异常
    to.credit(amount);
}
// ❌ InsufficientFundsException是检查型异常,默认不回滚

失效原因@Transactional默认只对运行时异常(RuntimeException)和错误(Error)回滚。

源码DefaultTransactionAttribute):

java 复制代码
public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error); // 默认策略
}

解决方案

java 复制代码
// 方案1:指定回滚异常
@Transactional(rollbackFor = {InsufficientFundsException.class, 
                               RuntimeException.class})
public void transferMoney(Account from, Account to, BigDecimal amount) 
        throws InsufficientFundsException {
    // ...
}

// 方案2:抛出自定义运行时异常
@Transactional
public void transferMoney(Account from, Account to, BigDecimal amount) {
    try {
        from.debit(amount);
    } catch (InsufficientFundsException e) {
        throw new BusinessRuntimeException("余额不足", e);
    }
    to.credit(amount);
}

3.4 场景4:非public方法

java 复制代码
@Service
public class ReportService {
    
    @Transactional
    void generateReport() { // ❌ 包私有方法
        // 事务失效
    }
    
    @Transactional
    protected void sendReport() { // ❌ protected方法
        // 事务失效
    }
    
    @Transactional
    private void saveReport() { // ❌ 私有方法
        // 事务失效
    }
}

失效原因 :Spring AOP基于代理,默认使用JDK动态代理(接口)或CGLIB(继承),都只能代理public方法。非public方法无法被代理,拦截器无法织入。

源码验证

java 复制代码
// AbstractFallbackTransactionAttributeSource
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
    // 非public方法直接返回null(事务属性)
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
    }
    // ...
}

解决方案

java 复制代码
// 方案:全部改为public
@Transactional
public void generateReport() { // ✅ public方法
    // 事务生效
}

3.5 场景5:final方法(CGLIB)**

java 复制代码
@Service
public class EmailService {
    
    @Transactional
    public final void sendEmail(Email email) { // ❌ final方法
        // CGLIB无法重写,事务失效
    }
}

失效原因 :CGLIB通过重写方法 实现代理。final方法不可重写,代理类无法覆盖,自然无法织入增强逻辑。

字节码层面观察

java 复制代码
// CGLIB生成的代理类
public class EmailService$$EnhancerByCGLIB extends EmailService {
    // 重写所有非final public方法
    @Override
    public void someMethod() { ... }
    
    // final方法未被重写,直接继承父类实现
    // 因此调用时直接执行父类方法,无拦截器
}

解决方案

java 复制代码
// 移除final
@Transactional
public void sendEmail(Email email) { // ✅ 非final
    // 事务生效
}

3.6 场景6:事务传播行为配置错误

java 复制代码
@Service
public class PaymentService {
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void deductBalance(Account account, BigDecimal amount) {
        account.setBalance(account.getBalance().subtract(amount));
        accountRepository.save(account);
    }
}

@Service
public class OrderService {
    
    @Autowired
    private PaymentService paymentService;
    
    @Transactional
    public void createOrder(Order order) {
        orderRepository.save(order);
        
        try {
            paymentService.deductBalance(order.getAccount(), order.getAmount());
        } catch (InsufficientFundsException e) {
            log.error("余额不足");
            // 订单已保存,但deductBalance在独立事务
            // 异常不会导致订单回滚 ❌ 数据不一致
        }
    }
}

问题分析

  • REQUIRES_NEW创建新事务,与外层事务无关
  • 内部事务回滚不会导致外部事务回滚
  • 可能导致数据不一致

正确做法

java 复制代码
@Transactional
public void createOrder(Order order) {
    orderRepository.save(order);
    
    // 不要使用REQUIRES_NEW,除非明确需要独立提交
    paymentService.deductBalance(order.getAccount(), order.getAmount()); // 默认REQUIRED
}

3.7 场景7:多线程调用

java 复制代码
@Service
public class AsyncService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional
    public void batchUpdate(List<User> users) {
        users.parallelStream().forEach(user -> {
            // 在新线程中执行
            updateUser(user); // ❌ 新线程无事务上下文
        });
    }
    
    @Transactional
    public void updateUser(User user) {
        userRepository.save(user);
    }
}

失效原因 :事务上下文绑定到当前线程 (ThreadLocal)。新线程没有继承事务,导致每个updateUser()在独立事务(或无事务)中执行。

线程模型

java 复制代码
Thread-1 (主线程)
  └─▶ TransactionStatus tx = TransactionAspectSupport.currentTransactionStatus();
      └─▶ 存储在ThreadLocal中
      
Thread-2 (parallelStream线程)
  └─▶ TransactionAspectSupport.currentTransactionStatus();
      └─▶ ThreadLocal为空 → 无事务

解决方案

方案1:避免在新线程中使用事务

java 复制代码
@Transactional
public void batchUpdate(List<User> users) {
    // 单线程处理
    users.forEach(user -> {
        updateUser(user); // 在同一线程
    });
}

方案2:手动传递事务(不推荐)

java 复制代码
// 复杂且易出错,应避免

方案3:使用编程式事务

java 复制代码
@Autowired
private TransactionTemplate transactionTemplate;

public void batchUpdate(List<User> users) {
    users.parallelStream().forEach(user -> {
        transactionTemplate.execute(status -> {
            userRepository.save(user);
            return null;
        });
    });
}

四、最佳实践与规避策略

4.1 事务方法设计原则

原则1:入口方法必须是public

java 复制代码
@Service
public class OrderApplicationService {
    
    @Transactional
    public void createOrder(CreateOrderCommand command) { // ✅ 入口public
        orderDomainService.validate(command);
        orderRepository.save(command.toOrder());
        eventPublisher.publish(new OrderCreatedEvent(...));
    }
}

// 领域服务方法可以是private(不暴露事务)
@Service
public class OrderDomainService {
    private void validate(CreateOrderCommand command) { // ✅ 私有,被public方法调用
        // 业务校验
    }
}

原则2:不要在事务中远程调用

java 复制代码
@Transactional
public void createOrder(Order order) {
    orderRepository.save(order);
    
    // ❌ 远程调用可能超时,导致事务长时间持有数据库连接
    paymentClient.deduct(order); // 可能阻塞10秒
    
    // ✅ 拆分到事务外
}

// 正确做法
public void createOrder(Order order) {
    transactionTemplate.execute(status -> {
        orderRepository.save(order);
    });
    paymentClient.deduct(order); // 事务外调用
}

原则3:事务粒度最小化

java 复制代码
// ❌ 大事务
@Transactional
public void processAll() {
    step1(); // 耗时1秒
    step2(); // 耗时2秒
    step3(); // 耗时3秒
}
// 持有锁6秒

// ✅ 小事务
public void processAll() {
    transactionTemplate.execute(status -> step1());
    transactionTemplate.execute(status -> step2());
    transactionTemplate.execute(status -> step3());
}
// 各自持有锁1、2、3秒

4.2 自调用问题的终极解决方案

方案:应用服务层(Application Service)模式

java 复制代码
// 接口层(无事务)
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    
    @Autowired
    private OrderApplicationService appService;
    
    @PostMapping
    public ResponseEntity<Void> create(@RequestBody CreateOrderDTO dto) {
        appService.createOrder(dto);
        return ResponseEntity.ok().build();
    }
}

// 应用服务层(事务边界)
@Service
public class OrderApplicationService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private InventoryService inventoryService;
    
    @Transactional // ✅ 事务在应用服务层
    public void createOrder(CreateOrderDTO dto) {
        Order order = Order.from(dto);
        orderRepository.save(order);
        
        // 即使InventoryService是同Bean,通过@Autowired调用代理
        inventoryService.deduct(order); // ✅ 通过代理调用,事务生效
    }
}

// 领域服务层(无事务)
@Service
public class InventoryService {
    
    @Autowired
    private InventoryRepository inventoryRepository;
    
    // 不需要事务,由调用方控制
    public void deduct(Order order) {
        Inventory inventory = inventoryRepository.findByProductId(order.getProductId());
        inventory.deduct(order.getQuantity());
        inventoryRepository.save(inventory);
    }
}

架构优势

  • 明确事务边界在应用服务层
  • 领域服务无事务依赖,可复用
  • 通过@Autowired调用,天然使用代理,避免自调用

4.3 异常处理策略

策略1:定义业务异常体系

java 复制代码
// 业务异常基类(运行时)
public class BusinessException extends RuntimeException {
    private final String errorCode;
    private final String errorMessage;
    
    public BusinessException(String errorCode, String errorMessage) {
        super(errorMessage);
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }
}

// 具体异常
public class InsufficientFundsException extends BusinessException {
    public InsufficientFundsException() {
        super("INSUFFICIENT_FUNDS", "余额不足");
    }
}

策略2:统一异常转换

java 复制代码
@Service
public class ExceptionTranslator {
    
    public <T> T translate(Supplier<T> action) {
        try {
            return action.get();
        } catch (SQLException e) {
            throw new DataAccessException("数据库异常", e);
        } catch (RemoteException e) {
            throw new ServiceInvocationException("远程调用失败", e);
        }
    }
}

// 使用
@Transactional
public void createOrder(Order order) {
    exceptionTranslator.translate(() -> {
        orderRepository.save(order);
        return null;
    });
}

4.4 验证事务是否生效

测试代码

java 复制代码
@SpringBootTest
public class TransactionTest {
    
    @Autowired
    private UserService userService;
    
    @Test
    public void testTransactionRollback() {
        assertThrows(RuntimeException.class, () -> {
            userService.saveWithException(new User("test"));
        });
        
        // 验证数据未插入(事务回滚)
        assertEquals(0, userRepository.count());
    }
}

@Service
public class UserService {
    
    @Transactional
    public void saveWithException(User user) {
        userRepository.save(user);
        throw new RuntimeException("测试回滚");
    }
}

五、总结:@Transactional失效自查清单

问题场景 原因 解决方案 验证方式
同类调用 绕过代理 注入自身/重构分层 日志打印代理类名
异常被吞 未抛出 重新抛出/setRollbackOnly 断点调试
检查型异常 默认不回滚 rollbackFor=Exception.class 单元测试
非public方法 无法代理 改为public 查看代理类
final方法 CGLIB无法重写 移除final 反编译代理类
多线程 ThreadLocal隔离 单线程/编程式事务 打印线程ID
REQUIRES_NEW 独立事务 谨慎使用 检查传播行为
传播配置错误 事务分离 使用REQUIRED 验证数据一致性

核心口诀:事务靠代理,代理要穿透,异常需传播,public是前提。

相关推荐
what丶k22 分钟前
深入解析Redis数据持久化:RBD机制原理、实操与生产最佳实践
数据库·redis·缓存
惊讶的猫2 小时前
探究StringBuilder和StringBuffer的线程安全问题
java·开发语言
jmxwzy2 小时前
Spring全家桶
java·spring·rpc
Halo_tjn2 小时前
基于封装的专项 知识点
java·前端·python·算法
Fleshy数模3 小时前
从数据获取到突破限制:Python爬虫进阶实战全攻略
java·开发语言
像少年啦飞驰点、3 小时前
零基础入门 Spring Boot:从“Hello World”到可上线的 Web 应用全闭环指南
java·spring boot·web开发·编程入门·后端开发
苍煜3 小时前
万字详解Maven打包策略:从基础插件到多模块实战
java·maven
有来技术3 小时前
Spring Boot 4 + Vue3 企业级多租户 SaaS:从共享 Schema 架构到商业化套餐设计
java·vue.js·spring boot·后端
东东5163 小时前
xxx医患档案管理系统
java·spring boot·vue·毕业设计·智慧城市
Dreamboat-L3 小时前
Redis及其两种持久化技术详解
数据库·redis·缓存