SpringMVC 异常处理与拦截器

第一部分:Java 异常体系回顾

1.1 异常的继承体系

⭐ 老师强调 :所有类的顶层父类是 Object,异常体系的起点是 Throwable。

复制代码
Object
  └── Throwable
        ├── Error (错误)
        └── Exception (异常)
              ├── 编译时异常 (Checked Exception)
              └── RuntimeException (运行时异常 / Unchecked Exception)
类型 特点 示例
Error(错误) 代码根本性逻辑问题,无法通过修改程序解决,如系统崩溃 VirtualMachineError、OutOfMemoryError
Exception(异常) 运行过程中产生的问题,可以通过程序逻辑改变来规避 除零异常、空指针异常

⭐ 老师强调:

  • 错误:如系统级硬伤,无法挽回。
  • 异常 :如除零(10/0),可通过改代码(如改除号为乘号或加判断)来避免。
  • 日常开发中,Error 很少见,重点处理 Exception。

1.2 异常的两种分类

分类 特点 常见异常(各记 5~8 个)
编译时异常 编译器强制要求处理(捕获或声明抛出),否则无法编译通过 ClassNotFoundException(类找不到)、SQLException(SQL异常)、IOException(IO异常)、NoSuchMethodException(方法找不到)、DataFormatException(日期格式异常)、FileNotFoundException(文件找不到)
运行时异常 编译可以通过,运行期间才暴露,不强制要求处理 NullPointerException(空指针)、ClassCastException(类型转换)、IndexOutOfBoundsException(索引越界)、ArithmeticException(算术异常,如除零)、NoSuchElementException(元素找不到)

⭐ 老师强调:

  • 除 RuntimeException 及其子类外,Exception 下的其他类均为编译时异常。
  • 区分关键:看类是否在 RuntimeException 节点下。
  • 初学者不懂业务场景时,可以把异常名贴给 AI,让 AI 写"业务场景案例"辅助理解。

1.3 异常的处理方式

⭐ 老师强调 :异常处理目前有且仅有两种形式:向上抛出 和 try-catch 捕获。

方式 说明
向上抛出 使用 throw 关键字,可在被调用方抛出,也可在调用方抛出。若一路不处理,最终抛给 JVM,程序终止。
try-catch 在当前代码块中捕获并处理异常,处理完后代码继续向下执行。

1.4 自定义异常

⭐ 老师强调:当系统已有的编译/运行时异常无法满足特定业务场景(如电商库存不足)时,需要自定义异常。

规范:

  1. 必须继承已有的异常类 (99% 继承 RuntimeException)。

  2. 通过 super(message) 传递异常提示信息。

  3. 在业务代码中使用 throw new 自定义异常("提示信息") 主动抛出。

    // 自定义异常示例
    public class StockException extends RuntimeException {
    public StockException() { super(); }
    public StockException(String message) { super(message); }
    public StockException(String message, Throwable cause) { super(message, cause); }
    public StockException(Throwable cause) { super(cause); }
    }

    // 使用
    if (stock <= 0) {
    throw new StockException("库存不足" + stock);
    }

第二部分:SpringMVC 统一异常处理

2.1 异常抛出机制

⭐ 老师强调 :在 Controller 调用 Service,Service 调用 Dao 的层级结构中,若底层不处理异常,异常会一级级向上抛出 ,最终由 DispatcherServlet 接收。

复制代码
Dao 抛出异常 → Service → Controller → DispatcherServlet

2.2 统一异常处理的价值

⭐ 老师强调 :普通用户看不懂 404、500 等错误堆栈。"家丑不可外扬" ,必须把系统内部错误转换为友好的提示页面(如"网站正在维护,请联系管理员")。

2.3 实现步骤

步骤一:自定义异常类

复制代码
public class SysException extends Exception {
    private String message;

    public SysException(String message) {
        this.message = message;
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

步骤二:实现异常处理器接口

⭐ 核心强调 :必须实现 Spring MVC 提供的 HandlerExceptionResolver 接口,重写 resolveException 方法,并交给框架管理。

复制代码
public class SysExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, 
                                         HttpServletResponse response, 
                                         Object handler, 
                                         Exception e) {
        // 1. 打印异常信息到控制台
        e.printStackTrace();
        
        // 2. 判断异常类型并做转换
        SysException exception = null;
        if (e instanceof SysException) {
            exception = (SysException) e;
        } else {
            exception = new SysException("系统正在维护,请联系管理员");
        }

        // 3. 封装数据并设置跳转页面
        ModelAndView mv = new ModelAndView();
        mv.addObject("errorMsg", exception.getMessage());
        mv.setViewName("error");
        return mv;
    }
}

步骤三:配置异常处理器

复制代码
<!-- 配置异常处理器 -->
<bean id="sysExceptionResolver" class="com.qcbyjy.demo3.SysExceptionResolver" />

步骤四:编写友好提示页面(error.jsp)

复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>错误提示页面</title>
</head>
<body>
    <h3>错误的友好提示页面</h3>
    ${errorMsg}
</body>
</html>

第三部分:SpringMVC 拦截器

3.1 拦截器概述

⭐ 老师强调:拦截器用于对**处理器(Controller 方法)**进行预处理和后处理。它是 AOP(面向切面编程)思想的一种实现。

拦截器链:多个拦截器按定义顺序结成一条链,请求被拦截时,会按顺序依次执行。

3.2 拦截器 vs 过滤器

对比维度 过滤器(Filter) 拦截器(Interceptor)
规范 属于 Servlet 规范,任何框架都能用 Spring MVC 框架独有
作用范围 配置 /* 可拦截所有资源(HTML、CSS、JS等) 仅针对 Controller 中的方法
层级 Java Web 层面(底层,大范围) Spring MVC 层面(框架内,局部)
实现 实现 Filter 接口 实现 HandlerInterceptor 接口

⭐ 老师强调 :过滤器像"大范围底层拦截",拦截器局限在框架内某一块业务。不要混淆两者。

3.3 自定义拦截器

步骤一:实现 HandlerInterceptor 接口

复制代码
public class MyInterceptor2 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) throws Exception {
        System.out.println("拦截器preHandle方法执行了...");
        // return true 放行;return false 拦截不放行
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler, 
                           ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法执行了...");
        // 也可以进行页面跳转,但会导致 controller 原本跳转的页面不显示
    }

    @Override
    public void afterCompletion(HttpServletRequest request, 
                                HttpServletResponse response, 
                                Object handler, 
                                Exception ex) throws Exception {
        System.out.println("afterCompletion方法执行了...");
        // 此时无法再进行页面跳转
    }
}

步骤二:在 springmvc.xml 中配置拦截器

复制代码
<mvc:interceptors>
    <!-- 配置第一个拦截器 -->
    <mvc:interceptor>
        <!-- 该拦截器拦截哪些资源 -->
        <mvc:mapping path="/dept/**"/>
        <!-- 哪些资源不想拦截 -->
        <!-- <mvc:exclude-mapping path="/dept/test.do"/> -->
        <!-- 拦截器对象 -->
        <bean class="com.qcbyjy.demo4.MyInterceptor2"/>
    </mvc:interceptor>

    <!-- 配置第二个拦截器 -->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.qcbyjy.demo4.MyInterceptor3"/>
    </mvc:interceptor>
</mvc:interceptors>

⭐ 老师强调:

  • 排除拦截时,若拦截路径写得很宽(如 /**),只需用 exclude-mapping 摘掉个别不需要拦的方法。
  • 配置多个拦截器时,后一个可简化配置。

3.4 HandlerInterceptor 接口的方法详解

方法 执行时机 关键点
preHandle Controller 方法执行前 return true 放行(执行下一个拦截器或 Controller 方法);return false 拦截,后续不执行。可使用 request/response 跳转页面。
postHandle Controller 方法执行后 ,JSP 视图执行前 若在此处使用 request/response 跳转页面,Controller 原本跳转的页面将不会显示。
afterCompletion JSP 视图执行后 此时不能再跳转页面。

单个拦截器执行流程:

复制代码
preHandle → Controller 方法 → postHandle → JSP 视图渲染 → afterCompletion

多个拦截器执行流程(A、B 两个拦截器):

复制代码
A.preHandle → B.preHandle → Controller 方法 → B.postHandle → A.postHandle → JSP 视图渲染 → B.afterCompletion → A.afterCompletion

附录:核心易错点速查表

类别 易错点 正确理解
异常分类 把 RuntimeException 当成编译时异常 RuntimeException 是运行时异常,编译时可通
处理方式 以为可以 try-catch 或 throw 之外的其他方式 异常处理有且仅有两种:抛出 和 try-catch
Error vs Exception 混淆 Error 和 Exception Error 无法通过代码解决,Exception 可以通过逻辑规避
自定义异常 继承 Object 或 Exception 之外没有规范的类 99% 继承 RuntimeException
异常处理器 自定义异常后不注册给框架 必须实现 HandlerExceptionResolver 并配置到 springmvc.xml
拦截器范围 以为拦截器能拦所有请求 拦截器只拦截 Controller 方法,不能拦截静态资源
放行机制 preHandle 返回 false 仍然希望执行 返回 false 后所有后续全部终止
afterCompletion 在此方法中做页面跳转 此时不能跳转页面
排除拦截 想拦住少数,结果把要拦的全列出来 拦截写宽泛(/**),用 exclude-mapping 排除个别的
拦截器/过滤器 混淆两者 过滤器在 JavaWeb 层(作用大),拦截器在 SpringMVC 层(作用小)

⭐ 老师总结 :SpringMVC 的统一异常处理通过 HandlerExceptionResolver 将程序异常转化为友好的用户提示页;拦截器通过 HandlerInterceptor 在 Controller 方法执行前后插入逻辑(如登录校验、日志打印)。拦截器只针对 Controller 方法,而过滤器可以拦截任何资源,两者职责和层级完全不同,务必分清。

相关推荐
知守观2 小时前
设计模式实战:从真实老项目中看标准模板方法与工厂的落地
java·spring·设计模式
橙露3 小时前
git简明教程以及资源分享
大数据·git
数字新视界4 小时前
一体化机柜在数据中心运维中的创新应用与优势分析
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
FinelyYang4 小时前
考试系统简答题 AI 阅卷:覆盖度驱动、异步评分与人机协同落地实践
java·大数据·人工智能
SelectDB4 小时前
从 StarRocks 走向 Apache Doris:如何实现引擎平滑替换与架构升级?
大数据·数据库·数据分析
\光辉岁月/4 小时前
3.spring5-AOP
spring·代理模式·aop·spring5
大大大大晴天4 小时前
每天认识一个组件:JanusGraph
大数据
东华果汁哥4 小时前
【Spring AI 实战】从零开始调用大模型接口,构建 Java AI 应用
java·人工智能·spring
数字新视界6 小时前
信创机房动环监控技术在智能运维中的应用与优势分析
大数据·服务器·数据库·动环监控系统·动力与环境监控系统