java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring AOP声明式事务管理

Spring如何使用AOP实现声明式事务管理

作者:冰糖心书房

Spring 使用 AOP 实现声明式事务管理是其最强大的功能之一,这篇文章小编将详细拆解这个过程,从高层概念到底层原理,感兴趣的小伙伴可以参考一下

一、 核心思想:代理模式 + AOP

想象一下,你是一位非常重要的业务专家(你的 Service 层代码),你的工作是处理核心业务逻辑(比如转账、更新用户信息)。但每次处理业务前,你都需要做一些准备工作(开启事务),结束后还要做一些收尾工作(提交或回滚事务)。这些准备和收尾工作繁琐、重复,而且和你的核心业务关系不大。

声明式事务管理的目标就是:让你这位专家只专注于核心业务,把所有繁琐的事务管理工作交给一个“管家”来处理。

这个“管家”就是 Spring AOP 创建的代理对象 (Proxy)。

AOP (Aspect-Oriented Programming, 面向切面编程) 在这里扮演的角色就是:

二、 详细工作流程(一步步拆解)

让我们跟踪一个对 @Transactional 方法的调用,看看 Spring 内部到底发生了什么。

场景:一个 UserController 调用一个 UserService 的 updateUser() 方法,该方法被 @Transactional 注解。

// 业务接口
public interface UserService {
    void updateUser(User user);
}

// 业务实现类
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    @Transactional // <--- 关键注解
    public void updateUser(User user) {
        // 核心业务逻辑
        userRepository.update(user);
        // 假设这里如果出错了,需要回滚
        if (user.getName() == null) {
            throw new IllegalArgumentException("Username cannot be null");
        }
    }
}

// 调用方
@RestController
public class UserController {
    @Autowired
    private UserService userService; // <--- 注意:这里注入的其实是代理对象

    @PostMapping("/user/update")
    public void updateUser(@RequestBody User user) {
        userService.updateUser(user);
    }
}

阶段一:应用启动时 - 创建代理

1.Bean 扫描:Spring 容器在启动时,会扫描所有的 Bean。

2.识别 @Transactional:当 Spring 扫描到 UserServiceImpl 时,它会发现这个类或其方法上存在 @Transactional 注解。

3.创建代理对象:Spring 认识到这个 Bean 需要被事务“增强”(Advised)。它不会直接创建 UserServiceImpl 的实例并放入容器,而是会通过 AOP 框架为它创建一个代理对象。

4.注入代理:当 UserController 通过 @Autowired 请求注入 UserService 时,Spring 容器会将上一步创建的那个代理对象注入给它,而不是原始的 UserServiceImpl 对象。UserController 对此毫不知情,它以为自己拿到的就是普通的 UserService。

阶段二:方法调用时 - 事务拦截

1.调用入口:UserController 调用 userService.updateUser(user)。

2.命中代理:这个调用首先命中的是代理对象的 updateUser 方法,而不是原始对象的。

3.事务拦截器 (TransactionInterceptor) 生效:代理对象的这个方法中包含了 AOP 的“通知”(Advice)。对于事务来说,这个通知的具体实现就是 TransactionInterceptor。这个拦截器就像一个警卫,拦住了这次调用。

4.开启事务:

5.调用原始方法:事务成功开启后,TransactionInterceptor 会通过反射调用原始 UserServiceImpl 对象的 updateUser 方法。

6.执行业务逻辑:此时,才真正开始执行你编写的核心业务代码,比如 userRepository.update(user)。所有这些数据库操作都将在上一步获取的那个被 Spring 管理的 Connection 上执行。

阶段三:方法执行后 - 提交或回滚

当原始的 updateUser 方法执行完毕后,控制权返回到 TransactionInterceptor。

1.正常执行完毕 (没有抛出异常):

2.抛出异常:

阶段四:收尾

无论事务是提交还是回滚,事务管理器最终都会将数据库连接释放回连接池。

三、 核心组件总结

@Transactional:元数据,告诉 AOP 在哪里以及如何应用事务。它本身不包含任何逻辑。

AOP 代理 (Proxy):由 Spring 动态创建的“管家”对象,是事务逻辑的实际入口。它包装了原始的业务对象。

事务拦截器 (TransactionInterceptor):AOP 通知(Advice)的具体实现,是代理对象中的核心逻辑。它负责在目标方法调用前后开启、提交或回滚事务。

事务管理器 (PlatformTransactionManager):一个统一的事务管理接口。Spring 通过它来适配不同的数据源技术(如 JDBC, JPA/Hibernate, JTA)。它负责执行真正的事务操作(begin, commit, rollback)。

四、 为什么 @Transactional 在某些情况下会失效?

理解了以上原理,就很容易明白为什么 @Transactional 会在以下情况失效:

1.方法不是 public 的:CGLIB 代理是通过继承来实现的,而 private 或 protected 方法无法被子类重写(override),所以 AOP 无法拦截。JDK 动态代理基于接口,更不存在非 public 方法。

2.同一个类中的方法调用(this 调用):

@Service
public class UserServiceImpl implements UserService {
    public void entryMethod() {
        this.updateUser(user); // <--- 失效!
    }

    @Transactional
    public void updateUser(User user) {
        // ...
    }
}

当外部调用 entryMethod() 时,进入的是 UserServiceImpl 的原始对象(如果 entryMethod 没有被代理)。然后 this.updateUser() 是一个对象内部的直接调用,它绕过了代理对象,直接调用了原始对象的 updateUser 方法,因此事务拦截器根本没有机会介入。

3.异常被 catch 掉了:

@Transactional
public void updateUser(User user) {
    try {
        // ... 抛出 RuntimeException 的代码
    } catch (Exception e) {
        // 异常被吞了,没有继续抛出
    }
}

事务拦截器是通过捕获异常来决定是否回滚的。如果异常被你的 try-catch 块“消化”了,拦截器就认为方法是正常执行完毕的,于是它会提交事务,而不是回滚。
通过这个机制,Spring 完美地将业务逻辑和非功能性的事务管理代码解耦,让开发者能更专注于业务价值的实现,代码也变得更加清晰和易于维护。

到此这篇关于Spring如何使用AOP实现声明式事务管理的文章就介绍到这了,更多相关Spring AOP声明式事务管理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文