SpringMVC06:SpringMVC 统一异常处理实战(完整案例 + 演示流程)
在 SpringMVC 项目开发中,如果不做统一异常处理,每个 Controller 都写 try-catch 会导致代码冗余、维护困难,且异常直接抛给用户会造成极差的体验。
本文将带你从零搭建 SpringMVC 全局异常处理 项目,包含自定义异常、全局异常处理器、全自动跳转(启动 Tomcat 自动访问,无异常跳成功页、有异常跳错误页) ,配套完整演示流程,可直接用于学习与作业展示。
一、项目环境
-
项目名称:SpringMVC06
-
包结构:com.qcby
-
技术栈:SpringMVC + Maven + Tomcat + JSP
-
核心目标:实现全局统一异常处理,自动跳转成功 / 错误页面
我这里创建的是MavwenWEB项目导入tomcat,项目的目录结构为:
SpringMVC06
├── src/main/java
│ └── com.qcby.demo3
│ ├── SysException.java // 自定义异常
│ ├── SysExceptionResolver.java // 全局异常处理器
│ └── RoleController.java // 控制器
├── src/main/resources
│ └── springmvc.xml // SpringMVC配置
├── webapp
│ ├── index.jsp // 自动跳转页
│ └── WEB-INF
│ ├── web.xml // Web配置
│ └── jsp
│ ├── suc.jsp // 成功页
│ └── error.jsp // 错误页
└── pom.xml // Maven依赖

二、完整代码实现
2.1 配置文件
2.1.1 pom.xml(Maven 依赖)
如果文件出现报错,可以删除依赖,自己手动补充
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qcby</groupId>
<artifactId>SpringMVC06</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<!-- 编译配置 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.1.2 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
2.1.3 springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 1. 包扫描:扫描你的controller -->
<context:component-scan base-package="com.qcby"/>
<!-- 2. 开启SpringMVC注解支持 -->
<mvc:annotation-driven/>
<!-- 3. 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 4. 配置全局异常处理器(核心!) -->
<bean id="sysExceptionResolver" class="com.qcby.demo3.SysExceptionResolver"/>
</beans>
2.2 自定义异常类 SysException.java
用于封装业务异常信息,区分系统异常与业务异常。
package com.qcby.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;
}
}
2.3 全局异常处理器 SysExceptionResolver.java
捕获所有 Controller 抛出的异常,统一处理并跳转页面。
package com.qcby.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) {
// 1. 控制台打印异常栈信息,方便开发排查
e.printStackTrace();
// 2. 定义最终要传递给页面的异常对象
SysException sysException;
// 3. 判断异常类型:如果是我们自定义的SysException,直接用;否则统一封装为系统异常
if (e instanceof SysException) {
sysException = (SysException) e;
} else {
// 非业务异常,给用户友好提示
sysException = new SysException("系统正在维护,请联系管理员");
}
// 4. 封装ModelAndView:携带异常信息 + 跳转错误页面
ModelAndView mv = new ModelAndView();
mv.addObject("errorMsg", sysException.getMessage());
mv.setViewName("error"); // 对应/WEB-INF/jsp/error.jsp
return mv;
}
}
2.4 Controller 层 RoleController.java
提供测试接口,支持正常访问与异常抛出。
package com.qcby.demo3;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 角色Controller:模拟异常场景
*/
@Controller
@RequestMapping("/role")
public class RoleController {
/**
* 方式1:传统try-catch手动处理(冗余写法,不推荐)
* @return 视图名
*/
@RequestMapping("/findAllOld.do")
public String findAllOld() {
try {
System.out.println("执行了...");
// 访问手动 try-catch 的接口 http://localhost:8080/role/findAllOld.do
// 模拟除零异常
// int a = 10 / 0;
} catch (Exception e) {
e.printStackTrace();
// 跳转到友好提示页面
return "error";
}
return "suc";
}
/**
* 方式2:全局异常处理器处理(推荐写法)
* @return 视图名
* @throws SysException 抛出自定义业务异常
*/
@RequestMapping("/findAll.do")
public String findAll() throws SysException {
System.out.println("执行了...");
// 场景1:直接抛出自定义异常(业务场景,比如数据不存在)
// throw new SysException("角色数据查询失败,请稍后再试");
// 场景2:模拟系统异常(除零),由处理器统一封装
int a = 10 / 0;
return "suc";
}
}
2.5 页面文件
2.5.1 成功页面 /WEB-INF/jsp/suc.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>操作成功</title>
</head>
<body>
<h3>操作成功!这是正常访问的成功页面</h3>
</body>
</html>
2.5.2 错误页面 /WEB-INF/jsp/error.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>
2.5.3 首页自动跳转 index.jsp(webapp 目录下)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<%
// 启动Tomcat自动跳转到你的Controller
response.sendRedirect("role/findAll.do");
%>
</body>
</html>
四、全自动演示流程(启动 Tomcat 即可)
说明:项目已配置 index.jsp 自动重定向,启动 Tomcat 后无需手动输入地址,浏览器会自动访问 role/findAll.do 接口。
我们只需要通过修改 RoleController 里的代码,切换不同场景的演示效果。
场景 1:无异常 → 自动跳成功页
目标
验证正常请求下,SpringMVC 能正确返回成功视图,跳转到 suc.jsp。
操作步骤
-
打开 RoleController.java,找到 findAll() 方法
-
注释掉所有异常代码,保留 return "suc";
@RequestMapping("/findAll.do")
public String findAll() throws SysException {
System.out.println("执行了Controller方法...");
// ================== 无异常场景:必须注释下面两行 ==================
// int a = 10 / 0; // 1. 注释掉这行:系统异常
// throw new SysException("角色数据查询失败,请稍后再试"); // 2. 注释掉这行:业务异常
// ==================================================================
return "suc";
}
-
启动 Tomcat 服务器
-
浏览器自动访问接口,页面显示:
✅ 操作成功!请求正常执行,无任何异常
场景 2:系统异常 → 自动跳错误页
目标
验证系统运行时异常(如 ArithmeticException),会被全局异常处理器捕获,并返回统一错误提示。
操作步骤
-
打开 RoleController.java,修改 findAll() 方法:
-
保留 int a = 10 / 0;(系统异常)
-
注释掉 throw new SysException(...)(业务异常)
-
@RequestMapping("/findAll.do")
public String findAll() throws SysException {
System.out.println("执行了Controller方法...");
// ================== 系统异常场景:仅打开这一行 ==================
int a = 10 / 0; // 打开这行:模拟除零异常
// throw new SysException("角色数据查询失败,请稍后再试"); // 注释掉这行
// =============================================================
return "suc";
}
-
重启 Tomcat 服务器
-
页面自动跳转并显示:
❌ 错误的友好提示页面错误信息:系统正在维护,请联系管理员
场景 3:业务异常 → 自动跳错误页
目标
验证主动抛出的自定义业务异常,会被全局异常处理器捕获,并返回自定义提示信息。
操作步骤
-
打开 RoleController.java,修改 findAll() 方法:
-
注释掉 int a = 10 / 0;(系统异常)
-
打开 throw new SysException(...)(业务异常)
-
@RequestMapping("/findAll.do")
public String findAll() throws SysException {
System.out.println("执行了Controller方法...");
// ================== 业务异常场景:仅打开这一行 ==================
// int a = 10 / 0; // 注释掉这行
throw new SysException("角色数据查询失败,请稍后再试"); // 打开这行:主动抛业务异常
// =============================================================
// return "suc"; // 抛异常后这行不会执行
}
-
重启 Tomcat 服务器
-
页面自动跳转并显示:
❌ 错误的友好提示页面错误信息:角色数据查询失败,请稍后再试
场景 4:传统 try-catch 方式对比(可选)
目标
对比「手动 try-catch」和「全局异常处理器」的区别,突出统一处理的优势。
操作步骤
-
手动访问地址:http://localhost:8080/role/findAllOld.do
- 方法代码:
@RequestMapping("/findAllOld.do")
public String findAllOld() {
try {
System.out.println("执行传统try-catch方法...");
int a = 10 / 0; // 模拟异常
} catch (Exception e) {
e.printStackTrace();
return "error"; // 手动捕获后直接跳错误页
}
return "suc";
}
- 观察效果:页面跳转到 error.jsp,但 errorMsg 为空(因为没有给 ModelAndView 传值)
对比结论
-
手动 try-catch: 每个方法都要写冗余代码,无法统一管理,错误信息无法传递到页面
-
全局异常处理器: 一次配置,所有 Controller 异常统一处理,错误信息可统一管理,用户体验更好
五、核心知识点总结
1. 全局异常处理优势
一次配置,所有 Controller 异常统一处理,消除冗余 try-catch,便于维护。
2. 异常处理流程
Controller 抛出异常 → 被 HandlerExceptionResolver 捕获 → 封装信息 → 跳转页面。
3. 两种异常区分
-
系统异常:如除零、空指针,统一返回友好提示
-
业务异常:主动抛出,返回自定义提示信息
4. 全自动跳转
通过 index.jsp 重定向,启动 Tomcat 自动访问接口,无需手动输入地址。
六、演示注意事项
-
每次修改 Controller 代码后必须重启 Tomcat
-
resources 目录需标记为 Resources Root(紫色)
-
错误页面必须开启 isELIgnored="false" 才能显示异常信息
-
Tomcat 上下文路径建议设置为 /,访问更简洁

浙公网安备 33010602011771号