第一部分: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 自定义异常
⭐ 老师强调:当系统已有的编译/运行时异常无法满足特定业务场景(如电商库存不足)时,需要自定义异常。
规范:
-
必须继承已有的异常类 (99% 继承
RuntimeException)。 -
通过
super(message)传递异常提示信息。 -
在业务代码中使用
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 方法,而过滤器可以拦截任何资源,两者职责和层级完全不同,务必分清。