本文主要是介绍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 中的切面与事务结合使用完整示例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!