Spring AOP 与事务管理进阶:传播行为原理与实战指南

​ 在构建企业级应用时,AOP(面向切面编程)和事务管理是解决横切关注点和数据一致性的核心技术。本文将结合手写 Spring 框架的实践,深入探讨 AOP 的实现原理以及事务传播行为的设计哲学,帮助开发者从 "会用" 到 "理解为什么用"。

一、AOP 实现:代理模式与职责链模式的精妙结合

1. 代理模式:实现方法拦截的基石

AOP 的核心是通过代理对象拦截目标方法的执行,在方法前后插入增强逻辑。在手写 Spring 中,ProxyFactory承担了代理对象的创建职责:

typescript 复制代码
// 手写Spring的ProxyFactory核心逻辑
public class ProxyFactory {
    public static Object createProxy(Object target, List<Advice> advices) {
        // 1. 判断目标类是否实现接口,选择代理方式
        if (target.getClass().getInterfaces().length > 0) {
            // JDK动态代理(基于接口)
            return createJdkProxy(target, advices);
        } else {
            // CGLIB代理(基于继承)
            return createCglibProxy(target, advices);
        }
    }
    
    private static Object createJdkProxy(Object target, List<Advice> advices) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy, method, args) -> {
                // 2. 创建职责链并执行增强逻辑
                MethodInvocation invocation = new ReflectiveMethodInvocation(
                    target, method, args, advices);
                return invocation.proceed();
            }
        );
    }
}

这段代码揭示了两个关键点:

  • 代理选择策略:根据目标类是否实现接口自动切换代理方式
  • 增强逻辑植入 :通过InvocationHandler拦截方法调用,将控制权交给职责链

2. 职责链模式:编排增强逻辑的艺术

MethodInvocation作为职责链的核心实现,按顺序执行所有增强逻辑:

kotlin 复制代码
// 手写Spring的MethodInvocation实现
public class ReflectiveMethodInvocation implements MethodInvocation {
    private final Object target;
    private final Method method;
    private final Object[] args;
    private final List<Advice> advices;
    private int currentAdviceIndex = -1; // 当前执行到的增强器索引
    
    public ReflectiveMethodInvocation(Object target, Method method, Object[] args, List<Advice> advices) {
        this.target = target;
        this.method = method;
        this.args = args;
        this.advices = advices;
    }
    
    @Override
    public Object proceed() throws Throwable {
        // 1. 如果所有增强器都执行完毕,则执行目标方法
        if (currentAdviceIndex == advices.size() - 1) {
            return method.invoke(target, args);
        }
        
        // 2. 否则执行下一个增强器
        Advice advice = advices.get(++currentAdviceIndex);
        
        // 3. 根据增强器类型执行不同逻辑
        if (advice instanceof MethodInterceptor) {
            return ((MethodInterceptor) advice).invoke(this);
        } else if (advice instanceof MethodBeforeAdvice) {
            ((MethodBeforeAdvice) advice).before(method, args, target);
            return proceed(); // 递归调用,执行下一个增强器
        } else if (advice instanceof AfterReturningAdvice) {
            Object returnValue = proceed(); // 先递归执行后续增强器和目标方法
            ((AfterReturningAdvice) advice).afterReturning(returnValue, method, args, target);
            return returnValue;
        }
        
        return proceed();
    }
}

这种设计的精妙之处在于:

  • 统一调用模型 :无论是前置、后置还是环绕通知,都通过proceed()方法递归调用
  • 灵活的增强顺序 :增强器列表的顺序决定了执行顺序,可通过Ordered接口控制
  • 异常处理能力:环绕通知可捕获目标方法的异常,实现异常增强

3. AOP 的典型应用:事务管理

在事务管理场景中,AOP 的应用体现得淋漓尽致:

java 复制代码
// 事务增强器实现
public class TransactionInterceptor implements MethodInterceptor {
    private final PlatformTransactionManager transactionManager;
    
    public TransactionInterceptor(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }
    
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // 1. 获取事务定义
        TransactionDefinition def = getTransactionDefinition(invocation.getMethod());
        
        // 2. 获取事务状态
        TransactionStatus status = transactionManager.getTransaction(def);
        
        try {
            // 3. 执行目标方法
            Object result = invocation.proceed();
            
            // 4. 正常提交事务
            transactionManager.commit(status);
            return result;
        } catch (Exception e) {
            // 5. 异常回滚事务
            if (shouldRollbackOn(e)) {
                transactionManager.rollback(status);
            }
            throw e;
        }
    }
}

通过 AOP 实现事务管理的优势:

  • 业务代码零侵入:无需在 Service 中显式编写事务代码
  • 统一事务管理:事务边界由 AOP 代理控制,避免分散管理
  • 灵活的事务配置:通过注解或 XML 配置事务属性,无需修改代码

二、事务传播行为:复杂事务场景的解决方案

1. 事务传播行为的本质

事务传播行为解决的核心问题是:在嵌套事务调用场景中,如何处理多个事务的关系。例如:

typescript 复制代码
@Service
public class OrderService {
    @Autowired
    private PaymentService paymentService;
    
    @Transactional
    public void createOrder() {
        // 创建订单
        paymentService.processPayment(); // 嵌套调用支付服务
    }
}

@Service
public class PaymentService {
    @Transactional
    public void processPayment() {
        // 处理支付
    }
}

此时processPayment()方法的事务应该如何与createOrder()的事务关联?这就需要事务传播行为来定义规则。

2. 核心传播行为详解

(1)PROPAGATION_REQUIRED(最常用)

规则:如果当前存在事务,则加入该事务;如果没有事务,则创建新事务。

应用场景:绝大多数业务方法都应使用此传播行为。

实现关键点

typescript 复制代码
// 手写Spring的事务管理器实现
public class DataSourceTransactionManager implements PlatformTransactionManager {
    @Override
    public TransactionStatus getTransaction(TransactionDefinition definition) {
        // 1. 判断当前是否存在事务
        boolean existingTransaction = isExistingTransaction();
        
        if (existingTransaction) {
            // 2. 如果存在事务且传播行为是REQUIRED,则加入当前事务
            if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED) {
                return new TransactionStatus(/* 加入当前事务 */);
            }
            // 其他传播行为处理...
        }
        
        // 3. 如果不存在事务,则创建新事务
        return startNewTransaction(definition);
    }
}

(2)PROPAGATION_REQUIRES_NEW

规则:总是创建新事务,如果当前存在事务,则挂起当前事务。

应用场景

  • 子操作需要独立提交 / 回滚(如支付日志记录)
  • 子操作需要更长的超时时间

实现关键点

scss 复制代码
// REQUIRES_NEW的关键逻辑
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
    // 1. 如果存在当前事务,先挂起
    if (existingTransaction) {
        SuspendedResourcesHolder suspendedResources = suspend();
        try {
            // 2. 创建新事务
            return startNewTransaction(definition);
        } catch (Exception e) {
            // 3. 恢复被挂起的事务
            resume(suspendedResources);
            throw e;
        }
    }
}

(3)PROPAGATION_NESTED

规则:如果当前存在事务,则在嵌套事务中执行;否则等同于 REQUIRED。

应用场景

  • 子操作需要独立回滚,但整体事务提交取决于主事务
  • 典型场景:批量导入时单条记录失败不影响整体导入

实现关键点

  • 嵌套事务通过保存点(Savepoint)实现
  • 子事务回滚只会回滚到保存点,不会影响主事务
  • 主事务回滚会导致所有嵌套事务回滚
scss 复制代码
// NESTED的关键逻辑
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
    if (existingTransaction) {
        // 1. 创建保存点
        Object savepoint = createSavepoint();
        return new NestedTransactionStatus(savepoint);
    } else {
        // 2. 不存在事务时等同于REQUIRED
        return startNewTransaction(definition);
    }
}

3. 事务传播行为的测试验证

通过以下测试代码可以验证传播行为的效果:

java 复制代码
// 测试PROPAGATION_REQUIRES_NEW的隔离性
@Test
public void testRequiresNew() {
    TransactionTemplate template1 = new TransactionTemplate(transactionManager);
    template1.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    
    template1.execute(status -> {
        // 主事务插入数据
        userDao.insert(new User("张三"));
        
        // 嵌套调用REQUIRES_NEW的方法
        TransactionTemplate template2 = new TransactionTemplate(transactionManager);
        template2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        template2.execute(status2 -> {
            userDao.insert(new User("李四"));
            // 子事务回滚
            status2.setRollbackOnly();
            return null;
        });
        
        // 主事务提交
        return null;
    });
    
    // 验证结果:数据库中只有"张三","李四"因回滚未插入
    List<User> users = userDao.findAll();
    assertEquals(1, users.size());
    assertEquals("张三", users.get(0).getName());
}

三、AOP 与事务传播的协同工作

1. 事务 AOP 切面的执行顺序

在 Spring 中,事务增强器的执行顺序非常关键:

java 复制代码
// 事务AOP切面的执行顺序控制
@Configuration
public class TransactionConfig implements Ordered {
    @Override
    public int getOrder() {
        // 确保事务切面在其他切面之后执行(值越大优先级越低)
        return Ordered.LOWEST_PRECEDENCE - 1;
    }
    
    // 其他配置...
}

这样设计的原因是:

  • 事务需要包裹其他增强逻辑(如权限校验、日志记录)
  • 确保事务边界的完整性

2. 嵌套事务调用的传播行为链

当多个带有事务注解的方法嵌套调用时,传播行为形成一个链条:

java 复制代码
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 外层事务
    methodB();
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
    // 独立事务
    methodC();
}

@Transactional(propagation = Propagation.NESTED)
public void methodC() {
    // 嵌套事务
}

这个调用链的事务行为:

  • methodA 创建主事务
  • methodB 挂起主事务,创建新事务
  • methodC 在 methodB 的事务中创建保存点,形成嵌套

四、实战建议与常见陷阱

1. 事务传播行为选择指南

传播行为 适用场景
PROPAGATION_REQUIRED 绝大多数业务方法(默认选择)
PROPAGATION_REQUIRES_NEW 子操作需要完全独立(如支付、日志)
PROPAGATION_NESTED 批量操作中允许部分失败(如批量导入)
PROPAGATION_SUPPORTS 可选事务的查询操作(提高性能)
PROPAGATION_NOT_SUPPORTED 非事务操作(如纯查询)
PROPAGATION_MANDATORY 必须在事务中执行的方法(确保调用者有事务)
PROPAGATION_NEVER 禁止事务的方法(如定时任务)

2. 常见陷阱与解决方案

(1)自调用导致 AOP 失效

typescript 复制代码
@Service
public class UserService {
    @Transactional
    public void updateUser() {
        // 自调用,事务增强失效
        this.createUser();
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void createUser() {
        // 创建用户
    }
}

解决方案

  • 通过AopContext.currentProxy()获取代理对象
  • 使用@Autowired注入自身

(2)异常未触发回滚

java 复制代码
@Transactional
public void transfer() throws Exception {
    // 转账操作
    throw new BusinessException("业务异常"); // 默认不会回滚
}

解决方案

  • 指定回滚异常类型:@Transactional(rollbackFor = Exception.class)
  • 确保异常能传播到 AOP 代理

五、总结:从设计模式到框架实现的升华

通过手写 Spring 框架的实践,我们深刻理解了:

  • AOP 的本质:通过代理模式和职责链模式实现方法拦截和增强
  • 事务传播的核心:定义嵌套事务的处理规则,确保数据一致性
  • 设计模式的力量:合理运用设计模式能构建出灵活且可扩展的框架

这些知识不仅帮助我们更好地使用 Spring,更教会我们如何用设计模式解决复杂的技术问题。在实际项目中,正确选择事务传播行为和合理使用 AOP,是构建高可靠性系统的关键。

如果这篇文章对大家有帮助可以点赞关注,你的支持就是我的动力😊!

相关推荐
tellmewhoisi2 分钟前
java8 List常用基本操作(去重,排序,转换等)
java·list
都叫我大帅哥32 分钟前
TOGAF应用架构阶段全解析:从理论到Java代码实战
java
Amagi.1 小时前
Java设计模式-建造者模式
java·设计模式·建造者模式
EmpressBoost1 小时前
谷粒商城170缓存序列化报错
java·spring·缓存
fouryears_234171 小时前
@PathVariable与@RequestParam的区别
java·spring·mvc·springboot
wxjlkh1 小时前
powershell 批量测试ip 端口 脚本
java·服务器·前端
萌新小白的逆袭2 小时前
《Maven 核心基础笔记(第一天)》
java·开发语言·spring
一念&2 小时前
Java泛型
java
掉鱼的猫2 小时前
Solon 整合 LiteFlow 规则引擎:概念与实战
java·workflow
她说..2 小时前
Stream API流学习总结
java