本篇包含 SpringMVC 统一异常处理原理、自定义异常、全局异常解析器;拦截器原理、三大方法、多拦截器执行顺序、拦截器和过滤器对比,配套代码与踩坑说明,适合复习、写博客。
一、SpringMVC 异常处理
1. 核心思路
Controller → Service → Dao 层层调用,异常向上抛出 ,最终交给前端控制器 DispatcherServlet,由异常处理器统一捕获、处理、跳转友好页面。
不推荐在每个 Controller 方法单独写
try-catch,代码冗余,统一异常处理器集中处理更优雅,是企业开发主流方案。
两种处理方式对比:
- 局部 try-catch(不推荐):在 Controller 内部捕获异常,自己处理页面跳转;分散、不好维护。
- 全局异常处理器(推荐) :实现
HandlerExceptionResolver接口,所有抛出的异常统一在这里接管。
2. 自定义异常类
业务中我们需要区分【系统内置异常】和【业务自定义异常】,自定义异常方便我们携带业务提示信息:
java
package cn.tx.demo3;
public class SysException extends Exception{
// 异常提示消息
private String message;
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
// 有参构造,抛出异常时直接传入提示文本
public SysException(String message) {
this.message = message;
}
}
✅ 说明:继承
Exception是受检异常,也可以继承RuntimeException运行时异常,抛出时不需要throws声明,项目里两种都常用。
3. 全局异常处理器(HandlerExceptionResolver)
实现接口 HandlerExceptionResolver,重写 resolveException 方法,这个方法在处理器抛出异常时自动触发。
java
package cn.tx.demo3;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SysExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception e) {
// 打印堆栈,方便后台排查日志
e.printStackTrace();
SysException exception;
// 判断:如果是我们自己抛出的业务异常,直接强转
if(e instanceof SysException){
exception = (SysException) e;
}else{
// 其余未知异常(空指针、除0异常等)统一包装成系统异常,返回通用提示,不要直接暴露原生异常信息给前端
exception = new SysException("系统正在维护,请联系管理员");
}
ModelAndView mv = new ModelAndView();
// 把异常信息存入域中,供页面获取
mv.addObject("errorMsg", exception.getMessage());
// 设置跳转的错误视图
mv.setViewName("error");
return mv;
}
}
4. SpringMVC 配置文件注册异常处理器
xml
<!-- 注册全局异常解析器 -->
<bean id="sysExceptionResolver" class="cn.tx.demo3.SysExceptionResolver"/>
5. Controller 模拟抛出异常
java
package cn.tx.demo3;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/role")
public class RoleController {
@RequestMapping("/findAll.do")
public String findAll() throws SysException {
System.out.println("执行了...");
// 模拟异常:除零异常
int a = 10/0;
// 也可以手动抛出自定义业务异常
// throw new SysException("角色数据查询失败!");
return "suc";
}
}
6. 错误提示页面 error.jsp
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>错误提示页面</title>
</head>
<body>
<h3>出错啦!友好提示页面</h3>
<p>${errorMsg}</p>
</body>
</html>
💡 补充拓展:SpringMVC 注解版全局异常(@ControllerAdvice + @ExceptionHandler) 现在 SSM、SpringBoot 更常用注解方式,不用实现接口,更加简洁:
java
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(SysException.class) public ModelAndView handleSysException(SysException e){ ModelAndView mv = new ModelAndView("error"); mv.addObject("errorMsg",e.getMessage()); return mv; } }
二、SpringMVC 拦截器(Interceptor)
1. 概述
拦截器是 SpringMVC 独有的,基于 AOP 思想,专门拦截 Controller 处理器方法,在 Controller 执行前后做预处理、后处理。
拦截器链:多个拦截器按配置顺序串联,依次执行。
✅ 拦截器 vs 过滤器(Filter)高频面试区别
表格
| 对比项 | Filter 过滤器 | Interceptor 拦截器 |
|---|---|---|
| 归属 | Servlet 规范,JavaWeb 原生 | SpringMVC 框架独有组件 |
| 拦截范围 | /* 可以拦截所有资源:jsp、静态资源、图片、css、Controller |
只拦截 Controller 处理器方法,静态资源默认不拦截 |
| 执行时机 | 在 DispatcherServlet之前执行 | 在 DispatcherServlet 内部,Controller 方法前后执行 |
| 使用场景 | 编码过滤、跨域、全局请求过滤 | 登录校验、权限判断、日志记录、请求计时 |
2. 自定义拦截器步骤
- 编写类,实现
HandlerInterceptor接口 - 重写需要的三个方法(preHandle /postHandle/afterCompletion)
- 在 springmvc.xml 中
<mvc:interceptors>配置拦截路径、放行路径
3. HandlerInterceptor 三大核心方法(重点)
java
package cn.tx.demo4;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor2 implements HandlerInterceptor {
/**
* preHandle:Controller方法【执行之前】执行
* 返回true:放行,继续执行后续拦截器/Controller
* 返回false:拦截,终止流程,Controller不会执行
* 👉 最常用!登录校验、权限判断写在这里
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器 preHandle 执行...");
// 示例:拦截后跳转到登录页
// request.getRequestDispatcher("/login.jsp").forward(request,response);
return true;
}
/**
* postHandle:Controller方法【执行完毕后】,视图渲染(jsp渲染)之前执行
* 可以修改 ModelAndView,修改跳转页面、修改域数据
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器 postHandle 执行...");
}
/**
* afterCompletion:【整个请求全部结束,视图渲染完成之后】执行
* 资源释放、日志记录,无论中间是否异常,只要preHandle返回true就会执行
* 此时不能再做页面跳转
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("拦截器 afterCompletion 执行...");
}
}
Controller 测试代码:
java
package cn.tx.demo4;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/dept")
public class DeptController {
@RequestMapping("/findAll.do")
public String findAll(){
System.out.println("controller 业务方法执行了...");
return "suc";
}
}
4. springmvc.xml 拦截器配置
xml
<!-- 拦截器配置 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- mapping:要拦截的路径 -->
<mvc:mapping path="/dept/**"/>
<!-- exclude-mapping:放行不拦截的路径,比如登录页面、静态资源 -->
<mvc:exclude-mapping path="/dept/login.do"/>
<!-- 拦截器对象 -->
<bean class="cn.tx.demo4.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
路径语法:
/**代表该路径下所有子请求;/*只匹配一级路径。
5. 多个拦截器(拦截器链)执行顺序
配置顺序:Interceptor2 → Interceptor3 执行顺序口诀:
preHandle 按配置顺序执行; postHandle、afterCompletion 逆序执行。
完整流程(全部放行,都 return true):
- Interceptor2.preHandle
- Interceptor3.preHandle
- Controller 方法执行
- Interceptor3.postHandle
- Interceptor2.postHandle
- JSP 视图渲染
- Interceptor3.afterCompletion
- Interceptor2.afterCompletion
⚠️ 特殊情况: 如果第二个拦截器 Interceptor3.preHandle() 返回 false:
- Interceptor3 的 postHandle、afterCompletion 都不会执行
- 只有前面已经放行过的拦截器(Interceptor2)的
afterCompletion会执行
xml
<mvc:interceptors>
<!-- 第一个拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/dept/**"/>
<bean class="cn.tx.demo4.MyInterceptor2"/>
</mvc:interceptor>
<!-- 第二个拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/dept/**"/>
<bean class="cn.tx.demo4.MyInterceptor3"/>
</mvc:interceptor>
</mvc:interceptors>
6. 拦截器经典业务场景:登录校验示例
java
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object user = request.getSession().getAttribute("loginUser");
if(user == null){
// 未登录,跳转到登录页
request.getRequestDispatcher("/login.jsp").forward(request,response);
return false;
}
// 已登录,放行
return true;
}
三、面试总结速记
- SpringMVC 异常处理 :异常向上抛,DispatcherServlet 交给
HandlerExceptionResolver;自定义异常区分业务错误,统一返回友好页面,避免直接把异常栈给前端。注解版@ControllerAdvice是现在主流写法。 - 拦截器三大方法 :
- preHandle:Controller 之前,返回 true 放行,登录权限校验
- postHandle:Controller 之后、视图渲染前,可以修改视图数据
- afterCompletion:页面渲染完成后,释放资源
- 多拦截器顺序:pre 顺序,post、after 逆序
- Filter 和 Interceptor 区别:Filter 属于 Servlet,拦截全部资源;Interceptor 属于 SpringMVC,只拦截 Controller。