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依赖

image

二、完整代码实现

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),会被全局异常处理器捕获,并返回统一错误提示。

操作步骤

  1. 打开 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";
}
  1. 重启 Tomcat 服务器

  2. 页面自动跳转并显示:

❌ 错误的友好提示页面错误信息:系统正在维护,请联系管理员

场景 3:业务异常 → 自动跳错误页

目标

验证主动抛出的自定义业务异常,会被全局异常处理器捕获,并返回自定义提示信息。

操作步骤

  1. 打开 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"; // 抛异常后这行不会执行
}
  1. 重启 Tomcat 服务器

  2. 页面自动跳转并显示:

❌ 错误的友好提示页面错误信息:角色数据查询失败,请稍后再试

场景 4:传统 try-catch 方式对比(可选)

目标

对比「手动 try-catch」和「全局异常处理器」的区别,突出统一处理的优势。

操作步骤

  1. 手动访问地址: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";
}
  1. 观察效果:页面跳转到 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 上下文路径建议设置为 /,访问更简洁

posted @ 2026-06-27 10:55  桃桃不淘1  阅读(3)  评论(0)    收藏  举报