JavaWeb Web事务管理&AOP
2025.05.29
JavaWeb学习之JavaWebWeb事务管理&AOP
一、Web事务管理
1.1 事务的概念
- 事务是指一组操作的集合,这些操作要么全部成功,要么全部失败。
- 事务的操作:
- 开启事务(一组操作开始前,开启事务) :start transaction / begin
- 提交事务(这组操作全部成功后,提交事务) :commit
- 回滚事务(中间任何一个操作出现异常,回滚事务) :rollback
- Spring事务管理:
- 注解:@Transactional
- 位置:业务(service)层的方法上、类上、接口上
- 作用:将当前方法交给spring进行事务管理,方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务
- spring事务管理日志:
1
2
3logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug - 案例:解散部门:删除部门,同时删除该部门下的员工
- DeptServiceIml.java
1
2
3
4
5
6
7// spring事务管理
public void delete(Integer id) {
deptMapper.deleteById(id);
empMapper.deleteByDeptId(id); // 根据部门id删除部门
}- EmpMapper.java
1
2
3
4
5/*
根据部门id删除员工
*/
void deleteByDeptId(Integer deptId);
1.2 事务进阶-rollbackFor
- rollbackFor
- 默认情况下,只有出现RuntimeException异常,才会回滚事务
- rollbackFor属性用于控制出现何种异常类型时回滚事务
- 示例:
1
2
3
4
5
6
7
8
9
10
11
12// spring事务管理
public void delete(Integer id) {
deptMapper.deleteById(id); // 删除部门
// 模拟异常
if (true) {
throw new Exception("模拟异常,回滚事务");
}
empMapper.deleteByDeptId(id); // 根据部门id删除员工
}
1.3 事务进阶-propagation
-
propagation
- 事务传播行为,指的就是当一个事物方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制
- 常用的事务传播行为
- REQUIRED:大部分情况下都是用该传播行为即可
- REQUIRES_NEW:当我们不希望事务之间相互影响时,可以使用该传播行为。比如:下订单前需要记录日志,不论订单保存成功与否,都需要保证日志记录能够记录成功
属性值 含义 REQUIRED [默认值]需要事务,有则加入,无则创建新事务 REQUIRES_NEW 需要新事务,无论有无,总是创建新事务 SUPPORTS 支持事务,有则加入,无则在无事务状态中运行 NOT_SUPPORTED 不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务 MANDATORY 必须有事务,否则抛异常 NEVER 必须有事务,否则抛异常 - -
案例:解散部门时,记录操作日志
- 需求:解散部门时,无论成功还是失败,都需要记录操作日志
- 步骤:
- 解散部门:删除部门、删除部门下的员工
- 记录日志到数据库表中
- DeptServiceImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// spring事务管理
public void delete(Integer id) {
try {
deptMapper.deleteById(id);
int i= 1/0;
empMapper.deleteByDeptId(id); // 根据部门id删除部门
} finally {
// 记录操作日志
DeptLog deptLog = new DeptLog();
deptLog.setCreateTime(LocalDateTime.now());
deptLog.setDescription("执行了解散部门的操作,此次解散的部门是" + id + "号部门");
deptLogService.insert(deptLog);
}
}- DeptLogServiceImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
public class DeptLogServiceImpl implements DeptLogService {
private DeptLogMapper deptLogMapper;
// 无论如何都会开启新事务
public void insert(DeptLog deptLog) {
deptLogMapper.insert(deptLog);
}
}
二、AOP
2.1 AOP概述
- AOP(Aspect Oriented Programming)面向切面编程,其实就是面向特定方法编程
- 场景
- 案例部分功能运行慢,定位执行耗时较长的业务方法,此时需要统计每一个业务方法的执行耗时

- 实现
- 动态代理是面向切面编程最主流的实现。而SpringAOP是Spring框架的高级技术, 旨在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程
- AOP入门:统计各个业务层方法执行耗时
- 导入依赖:在pom.xml中添加Spring AOP依赖
1
2
3
4<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>- 编写AOP程序:针对特定方法根据业务需要进行编程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 标识该类为AOP类
public class TimeAspect {
// 切入点表达式
public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
// 1. 记录开始时间
long begin = System.currentTimeMillis();
// 2. 调用原始方法运行
Object result = joinPoint.proceed();
// 3. 记录时间,计算方法执行时间
long end = System.currentTimeMillis();
log.info(joinPoint.getSignature() + "方法执行耗时:{}ms", end - begin);
return result;
}
}
2.2 AOP核心概念


- AOP会生成目标对象的增强版,也就是代理对象,之后Controller层在调用对象时调用的就不是原来的目标对象了,而是进行过方法增强的代理对象
2025.05.31
2.3 AOP通知类型
- 通知类型
- @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行
- @Before:前置通知,此注解标注的通知方法在目标方法前被执行
- @After:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
- @AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
- @AfterThrowing:异常后通知,此注解标注的通知方法发生异常后执行
- 注意事项
- @Around环绕通知需要自己调用ProceedingJoinPoint.proceed()来让原始方法执行,其他通知不需要考虑目标方法执行
- @Around环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值
- 引用切入点表达式
- @PointCut:该注解的作用是将公共的切入点表达式抽取出来,需要用到时引用该切点表达式即可
1
2
3
4
5
6
7
8
9
10
11
12
13
// 标识该类为AOP类
pubilc class MyAspect {
// 切入点表达式
public void pt() {} // 定义切入点
// 引用切入点表达式
public void before() {
log.info("before...")
}
}
2.4 通知顺序
- 通知顺序:当有多个切面的切入点都匹配到了目标方法,目标方法运行时,多个通知方法都会执行
- 执行顺序
- 不同切面类中,默认按照切面类的类名字母排序
- 目标方法前的通知方法:字母排名靠前的先执行
- 目标方法后的通知方法:字母排名靠前的后执行
- 用@Order(数字)加在切面类上来控制顺序
- 目标方法前的通知方法:数字小的先执行
- 目标方法后的通知方法:数字小的后执行
- 不同切面类中,默认按照切面类的类名字母排序
2.5 切入点表达式
- 切入点表达式:描述切入点方法的一种表达式
- 作用:主要用来决定项目中的哪些方法需要加入通知
- 常见形式
- execution(…):根据方法的签名来匹配
- @annotation(…):根据注解来匹配
2.5.1 execution
- execution主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,语法为:
1 | execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?) |
- 其中带?的表示可以省略
- 访问修饰符:如public、private等
- 包名.类名
- throws 异常:注意是方法上声明抛出的异常,不是实际抛出的异常
- 可以使用通配符描述切入点
- *:单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分
- …:多个连续的任意符号,可以通配任意层级的包或任意类型、任意个数的参数
- 注意事项
- 根据业务需要,可以使用且(&&)、或(||)、非(!)等逻辑运算符来组合多个切入点表达式
- 书写建议
- 所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配。如:查询类方法都是find开头,更新类方法都是update开头
- 描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性
- 在满足业务需要的前提下,尽量缩小切入点的匹配范围。如:包名匹配尽量不使用…使用*匹配单个包
2.5.2 @annotation
- @annotation主要用于匹配标识有特定注解的方法
1 |
1 | // 自定义注解MyLog |
2.6 连接点
- 在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等
- 对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint
- 对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型
- 示例
1 |
|
2.7 案例
-
需求: 将案例中增、删、改相关接口的操作日志记录到数据库表中
-
操作日志信息包含:操作人、操作时间、执行方法的全类名、执行方法名、方法运行时的参数、返回值、方法执行时长
-
操作日志表
1 | -- 操作日志表 |
- 操作日志实体类
1 | package com.itheima.pojo; |
- 操作日志Mapper
1 | package com.itheima.mapper; |
- 自定义注解Log
1 | package com.itheima.anno; |
- 切面类LogAspect
1 | package com.itheima.aop; |
