【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是前提。

相关推荐
Full Stack Developme几秒前
Spring 发展历史
java·后端·spring
组合缺一16 分钟前
Java 流程编排新范式 Solon Flow:一个引擎,七种节点,覆盖规则/任务/工作流/AI 编排全场景
java·spring·ai·solon·workflow·flow
largecode19 分钟前
企业号码认证可以线上办理吗?支持线上申请,设置来电显示品牌名
java·python·智能手机·微信公众平台·facebook·paddle·新浪微博
humcomm19 分钟前
2026年 Java 面试新特点
java·开发语言·面试
lili001223 分钟前
CC GUI 插件架构剖析:如何为 JetBrains IDE 打造完整的 AI 编程工作台
java·ide·人工智能·python·架构·ai编程
Royzst27 分钟前
学生信息管理案例
java
爱棋笑谦29 分钟前
单元测试简述
java
音符犹如代码36 分钟前
Docker 一键部署带有 TimescaleDB 插件的 PostgreSQL
java·运维·数据库·后端·docker·postgresql·容器
sleepcattt1 小时前
Java反射技术
java
小锋java12341 小时前
【技术专题】Spring AI 2.0 - Advisors —— 拦截器模式增强AI能力
java·人工智能