视频加载失败

JavaWeb Day05:全局异常、AOP 日志与管理模块

932 字
5 分钟
JavaWeb Day05:全局异常、AOP 日志与管理模块

模块与代码结构#

模块代码锚点
员工删除EmpController.delete(@RequestParam List<Integer> ids)+ EmpServiceImpl.delete(@Transactional(rollbackFor) + empMapper.deleteByIds + empExprMapper.deleteByEmpIds)
全局异常处理GlobalExceptionHandler(@RestControllerAdvice + @ExceptionHandler,DuplicateKeyException 截取 Duplicate entry 提示「xxx已存在」)
操作日志 AOPLogAspect(@Aspect + @Pointcut("execution(* com.ryuki.controller..*.*(..)) && !execution(* ...LogController.*(..))") + @Around)
班级管理 CRUDClazzController/Service/Mapper(分页 + case when 开班状态 + 删除前校验 + 动态 <set> 修改)
学员管理 CRUDStudentController/Service/Mapper(条件分页 <where>/<if> + <foreach> 批量删 + 学号生成 + 违纪处理阈值 30)
报表统计ReportController/Service(员工职位/性别 + 学员学历 case when + 班级人数 right join)

核心结论#

  1. 全局异常:@RestControllerAdvice(= @ControllerAdvice + @ResponseBody)统一捕获;@ExceptionHandler 可按异常类型分流(Exception 兜底 + 具体异常精准处理);DuplicateKeyException 解析 Duplicate entry 'xxx' for key ... 后返回「xxx已存在」。
  2. AOP 操作日志:@Pointcut 定义切入点(execution 表达式 + &&/||/! 组合排除);@Around + ProceedingJoinPoint(proceed() 放行 + getArgs() 参数 + getTarget() 目标类 + getSignature() 方法名);finally 中记录保证成功失败都记;日志插入失败 catch 掉只 warn,不影响主业务——与 EmpLog 的 try-finally 同事务方案形成对照。
  3. 操作人识别:无登录功能时从请求头 X-Emp-Id 读取(经 RequestContextHolder.getRequestAttributes() 拿到 ServletRequest);真实项目应从 JWT/ThreadLocal 登录态取。
  4. 班级删除业务校验:删除前 count 该班级下的学员数,大于 0 则抛业务异常,由全局异常处理器兜底返回 Result.error;无学员才真正删除。
  5. 动态 SQL 复用:<where> 自动去掉多余的 and/or,<if> 按条件拼接,<set> 用于动态更新,<foreach> 生成批量 in 的 id 列表;模糊匹配用 like concat('%', #{name}, '%'),即防注入的 #{} 写法。
  6. 报表 SQL:case when 把枚举数值转成中文(如学历);right join 保证没有学员的班级也显示为 0 人;配合 group by + count。
  7. 学号生成:年份 + 月份(%02d)+ 4 位随机数(%04d)。
  8. 事务:批量删除、违纪处理都加 @Transactional(rollbackFor = Exception.class);违纪扣分执行 violation_count+1、violation_score+score,累计扣满 30 分清零。

AOP 与事务的补充要点#

  • AOP 把日志这类横切逻辑从业务代码中抽离统一封装;Spring AOP 底层使用 JDK 动态代理或 CGLIB。切点(Pointcut)是匹配连接点(JoinPoint)的表达式。
  • 通知类型共五种:@Before、@After、@AfterReturning、@AfterThrowing、@Around,其中 @Around 最灵活,可控制目标方法是否执行并改写返回值。
  • Spring 事务有两种用法:编程式事务手动控制提交与回滚,声明式事务通过 @Transactional 声明,底层由 AOP 代理实现。
  • 事务传播行为共 7 种,常用的两种对比:REQUIRED 加入当前事务,随当前事务一并提交或回滚;REQUIRES_NEW 挂起当前事务并新建独立事务,两者各自提交、各自回滚。

踩坑与发现#

  1. countByClazzId 查错表:

    <!-- 当前写法:查 clazz 表自己 → id 存在恒为 1 → 永远抛「该班级下还有学员」→ 班级永远删不掉 -->
    <select id="countByClazzId" resultType="java.lang.Integer">
    select count(*) from clazz where id = #{id}
    </select>

    正确写法应查学员表:select count(*) from student where clazz_id = #{id}。

  2. logback 输出路径指向盘符根目录:logback.xml 第 15 行 <FileNamePattern>D:/tlias-%d{yyyy-MM-dd}-%i.log</FileNamePattern> 写的是 D 盘根目录,本机拒绝写入,mvn test 启动上下文直接失败(FileNotFoundException: D:\tlias-2026-08-15-0.log (拒绝访问)),mvn compile 不受影响。修复方向是改成相对路径,或用 logging.file 配置接管。

  3. AOP 切点要排除日志查询自身:切点若不排除 LogController,「查日志」这个请求也会被记录日志,递归触发死循环。

  4. @Around 忘记调用 proceed():目标方法不会执行也不会报错,环绕方法直接返回自己的返回值(可能为 null)。

  5. 删除接口参数风格:员工删除用 @RequestParam List<Integer> ids(?ids=1,2,3),学员删除用 @PathVariable List<Integer> ids(/students/1,2,3),两种风格都可用,注意与接口文档口径一致。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
JavaWeb Day05:全局异常、AOP 日志与管理模块
https://ryuki.icu/posts/javaweb-day05/
作者
RK
发布于
2026-08-15
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
RK
Long may the sunshine
公告
Long may the sunshine
分类
标签
最新动态
站点天气
天气加载中…
站点统计
文章
27
分类
6
标签
59
总字数
39,139
篝火已燃
0 天
最后活动
0 天前
每日一签