本文主要是介绍Spring 中的切面与事务结合使用完整示例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《Spring中的切面与事务结合使用完整示例》本文给大家介绍Spring中的切面与事务结合使用完整示例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考...
一、前置知识:Spring AOP 与 事务的关系
事务本质上就是一个“切面”
@Transactional
注解底层就是通过 AOP + 动态代理 实现的。- 当你在一个方法上加了
@Transactional
,Spring 会:- 创建代理对象
- 在方法执行前开启事务
- 方法成功返回时提交事务
- 方法抛出异常时回滚事务
二、核心组件
组件 | 作用 |
---|---|
PlatformTransactionManager | 事务管理器,负责开启、提交、回滚事务 |
TransactionDefinition | 事务定义(隔离级别、传播行为等) |
TransactionStatus | 当前事务状态 |
@ASPect | 定义切面 |
@Around | 环绕通知,控制方法执行全过程 |
三、完整示例:用切面实现事务控制
场景:
我们有一个 UserService
,其中 createUserWithOrder
方法需要:
- 保存用户
- 保存订单
- 如果任一步失败,整个操作回滚
我们将不用 @Transactional
,而是用一个切面来控制事务。
1. 添加依赖(pom.xml)
<dependencies> <dependency> <groupId&gChina编程t;org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </编程China编程dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> </dependencies>
2. 创建实体类
// User.Java public class User { private Long id; private String name; }
// Order.java public class Order { private Long id; private String productName; private Long userId; }
3. 创建 Service 方法(标记自定义注解)
@Service public class UserService { @Autowired private JdbcTemplate jdbcTemplate; // 自定义注解,表示此方法需要事务 @TransactionalAspect public void createUserWithOrder(String userName, String productName) { // 1. 保存用户 String userSql = "INSERT INTO users(name) VALUES(?)"; jdbcTemplate.update(userSql, userName); // 模拟异常:如果 productName 是 "fail",则抛出异常 if ("fail".equals(productName)) { throw new RuntimeException("订单创建失败!"); } // 2. 保存订单 String orderSql = "INSERT INTO orders(product_name, user_id) VALUES(?, ?)"; // 假设用户ID是自增,这里简化为查出来 Long userId = jdbcTemplate.queryForObject("SELECT MAX(id) FROM users", Long.class); jdbcTemplate.update(orderSql, productName, userId); } }
4. 创建自定义注解(用于标记需要事务的方法)
// TransactionalAspect.java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface TransactionalAspect { }
5. 编写切面:手动控制事务
@Aspect @Component public class TransactionAspect { @Autowired private PlatformTransactionManager transactionManager; @Around("@annotation(transactionalAspect)") public Object manageTransaction(ProceedingJoinPoint joinPoint, TransactionalAspect transactionalAspect) throws Throwable { // 1. 定义事务属性 DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); // 默认传播行为 // 2. 开启事务 TransactionStatus status = transactionManager.getTransaction(def); try { // 3. 执行目标方法 Object result = joinPoint.proceed(); // 4. 方法成功执行,提交事务 transactionManager.commit(status); System.out.println("事务提交成功"); return result; } catch (Exception e) { // 5. 发生异常,回滚事务 if (status != null && !status.isCompleted()) { transactionManager.rollback(status); System.out.println("事务已回滚,原因: " + e.getMessage()); } throw e; // 重新抛出异常 } } }
6. 测试 Controller
@RestController pjsublic class TestController { @Autowired private UsjserService userService; // 测试:成功场景 @GetMapping("/test/success") public String testSuccess() { userService.createUserWithOrder("张三", "iPhone"); return "用户和订单创建成功!"; } // 测试:失败场景(应回滚) @GetMapping("/test/fail") public String testFail() { try { userService.createUserWithOrder("李四", "fail"); } catch (Exception e) { return "创建失败,但事务已回滚!"; } return "success"; } }
四、测试验证
1. 启动应用
访问:
http://localhost:8080/test/success
- 结果:用户和订单都插入
http://localhost:8080/test/fail
- 结果:抛出异常,用户也不会被保存(事务回滚)
✅ 验证成功:即使用户先插入,但由于异常,整个事务回滚。
五、关键点解析
问题 | 解释 |
---|---|
为什么不用 @Transactional ? | 演示 AOP 如何手动控制事务,理解底层原理 |
PlatformTransactionManager 从哪来? | Spring Boot 自动配置(如 DataSourceTransactionManager ) |
@Around 是关键 | 它能控制方法执行前后,决定提交或回滚 |
异常必须 re-throw | 否则上层不知道失败,可能掩盖问题 |
status.isCompleted() | 防止重复提交或回滚 |
六、注意事项
- 切面失效问题:
- 不要在同一个类中调用被切面拦截的方法(内部调用不走代理)
- 事务传播行为:
- 当前例子是
REQUjsIRED
,更复杂的场景需考虑REQUIRES_NEW
等
- 当前例子是
- 性能:
- 事务越短越好,避免在事务中做耗时操作(如远程调用)
- 只读事务:
- 查询方法应使用
@Transactional(readOnly = true)
- 查询方法应使用
到此这篇关于Spring 中的切面与事务结合使用完整示例的文章就介绍到这了,更多相关Spring切面与事务结合内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!
这篇关于Spring 中的切面与事务结合使用完整示例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!