Spring MVC 之 异常处理

使用Spring MVC可以很灵活地完成数据的绑定和响应,极大的简化了Java Web的开发。但Spring MVC提供的便利不仅仅如此,使用Spring MVC还可以很便捷地完成项目中的异常处理、自定义拦截器以及文件上传和下载等高级功能。本章将对Spring MVC提供的这些高级功能进行讲解。

异常处理

简单异常处理器

如果希望对Spring MVC中所有异常进行统一处理,可以使用Spring MVC提供的异常处理器HandlerExceptionResolver接口。

Spring MVC内部提供了HandlerExceptionResolver的实现类SimpleMappingExceptionResolver。它实现了简单的异常处理,通过该实现类可以将不同类型的异常映射到不同的页面,当发生异常的时候,实现类根据发生的异常类型跳转到指定的页面处理异常信息。实现类也可以为所有的异常指定一个默认的异常处理页面,当应用程序抛出的异常没有对应的映射页面,则使用默认页面处理异常信息。

案例演示

下面通过一个案例演示SimpleMappingExceptionResolver对异常的统一处理,案例具体实现步骤如下所示。

java 复制代码
package com.lq.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Objects;

/**
 * @Author: Luqing Teacher
 * @CreateTime: 2025-03-13
 * @Description: ExceptionController
 * @Version: 1.0
 */


@Controller
public class ExceptionController {

    // 抛出空指针异常
    @RequestMapping("/showNullPointer")
    public void showNullPointer(){
        ArrayList<Object> list = new ArrayList<>();
        System.out.println(list.get(2));
    }

    // 抛出IO异常
    @RequestMapping("/showIOException")
    public void showIOException() throws FileNotFoundException {
        FileInputStream in = new FileInputStream("javaweb.xml");
    }

    // 抛出算术异常
    @RequestMapping("/showArithmetic")
    public void showArithmetic(){
        int a = 1/0;
    }
}

程序执行文件ExceptionController.java中的任意一个方法时,都会抛出异常。在异常发生时,如果要跳转到指定的处理页面,则需要在Spring MVC的配置文件spring-mvc.xml中使用SimpleMappingExceptionResolver指定异常和异常处理页面的映射关系。Spring MVC配置文件的部分配置如下所示。

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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 https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 配置 Spring MVC 要扫描的包 -->
    <context:component-scan base-package="com.lq.controller"/>
    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 配置注解驱动 -->
    <mvc:annotation-driven />
    <!--配置静态资源的访问映射,此配置中的文件,将不被前端控制器拦截 -->
    <mvc:resources mapping="/js/**" location="/js/" />
    <!-- 注入 SimpleMappingExceptionResolver 异常处理器-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!-- 定义特殊处理的异常,类名或完全路径名作为key,对应的异常页面名作为值-->
        <property name="exceptionMappings">
            <props>
                <prop key="java.lang.NullPointerException">nullPointerExp.jsp</prop>
                <prop key="java.io.IOException">IOExp.jsp</prop>
            </props>
        </property>
        <!-- 为所有异常定义默认的异常页面,value为默认的异常页面名-->
        <property name="defaultErrorView" value="defaultExp.jsp"/>
        <!-- value定义在异常处理页面中获取异常信息的变量名,默认为exception -->
        <property name="exceptionAttribute" value="exp"/>
    </bean>
</beans>

在文件spring-mvc.xml中,已经指定了异常类别对应的异常处理页面,接下来创建这些异常处理页面。在此不对异常处理页面做太多处理,只在页面中展示对应的异常信息。

接下来在webapp下创建nullPointerExp.jsp、IOExp.jsp、defaultExp.jsp页面分别如下:(内容修改成指定的页面)

XML 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>空指针异常处理页面</title></head>
<body>
空指针异常处理页面-----${exp}
</body>
</html>

结果如下:

从图中所示的信息可以看出,程序在抛出异常时,会跳转到异常类型对应的异常处理页面中。如果抛出的异常没有在Spring MVC的配置文件中指定对应的异常处理页面,那么程序会跳转到指定的默认异常处理页面。

自定义异常处理器

resolveException()方法

除了使用SimpleMappingExceptionResolver进行异常处理,还可以自定义异常处理器统一处理异常。通过实现HandlerExceptionResolver接口,重写异常处理方法resolveException()来定义自定义异常处理器。当Handler执行并且抛出异常时,自定义异常处理器会拦截异常并执行重写的resolveException()方法,该方法返回值是ModelAndView类型的对象,可以在ModelAndView对象中存储异常信息,并跳转到异常处理页面。

案例演示

接下来通过一个案例演示自定义异常处理器分类别处理自定义异常和系统自带的异常,案例具体实现步骤如下所示。在src\main\java目录,创建一个路径为com.lq.exception的包,并在包中创建自定义异常类MyException:

java 复制代码
package com.lq.exception;

/**
 * @Author: Luqing Teacher
 * @CreateTime: 2025-03-13
 * @Description:
 * @Version: 1.0
 */


public class MyException extends Exception {
    private String message;//异常信息
    public MyException(String message) {
        super(message);
        this.message = message;
    }

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

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

在ExceptionController类中,新增方法addData()用于抛出自定义异常,addData()方法的具体代码如下所示

java 复制代码
@RequestMapping("/addData")
public String addData() throws MyException {
    throw new MyException("新增数据异常!");
}

在com.lq.controller包下,创建名称为MyExceptionHandler的自定义异常处理器。在MyExceptionHandler类中重写resolveException()方法,用于判断当前异常是自定义异常还是系统自带的异常,根据异常的种类不同,resolveException()方法返回不同的异常信息。使用自定义异常处理器,需要先将自定义异常处理器注册到Spring MVC中。MyExceptionHandler类的部分代码如下所示。

java 复制代码
package com.lq.controller;

import com.lq.exception.MyException;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;

/**
 * @Author: Luqing Teacher
 * @CreateTime: 2025-03-13
 * @Description:
 * @Version: 1.0
 */

@Component
public class MyExceptionHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        String msg;
        if(ex instanceof MyException) {
            msg = ex.getMessage();
        }else {
            Writer out = new StringWriter();
            PrintWriter s = new PrintWriter(out);
            ex.printStackTrace(s);
            String sysMsg = out.toString();
            msg = "<h1>你个傻冒,又出错了吧!!</h1>" + sysMsg;
        }
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg", msg);
        modelAndView.setViewName("error.jsp");
        return modelAndView;
    }
}

访问showNullPointer,出现下列页面

​访问addData

异常处理注解

@ControllerAdvice注解的作用

从Spring 3.2开始,Spring提供了一个新注解@ControllerAdvice,@ControllerAdvice有以下两个作用。

• 注解作用在类上时可以增强Controller,对Controller中被@RequestMapping注解标注的方法加一些逻辑处理。

• @ControllerAdvice注解结合方法型注解@ExceptionHandler,可以捕获Controller中抛出的指定类型的异常,从而实现不同类型的异常统一处理。

案例演示

接下来通过一个案例演示使用注解实现异常的分类处理,具体实现步骤如下所示。

在com.lq.controller包下,创建名称为ExceptionAdvice的异常处理器。ExceptionAdvice类中定义2个处理不同异常的方法,其中doMyException()方法用来处理Handler执行时抛出的自定义异常,doOtherException()方法用来处理Handler执行时抛出的系统异常。

java 复制代码
package com.lq.controller;

import com.lq.exception.MyException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

/**
 * @Author: Luqing Teacher
 * @CreateTime: 2025-03-13
 * @Description:
 * @Version: 1.0
 */


@ControllerAdvice
public class ExceptionAdvice {

    @ExceptionHandler(value = MyException.class)
    public ModelAndView doMyException(Exception ex) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", ex.getMessage());
        mv.setViewName("error.jsp");
        return mv;
    }

    @ExceptionHandler(value = Exception.class)
    public ModelAndView doOtherException(Exception ex) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", "<h1>傻冒出错了吧!!哈哈!!</h1>");
        mv.setViewName("error.jsp");
        return mv;
    }

}

相关推荐
有梦想的攻城狮1 小时前
maven中的maven-antrun-plugin插件详解
java·maven·插件·antrun
_r0bin_3 小时前
前端面试准备-7
开发语言·前端·javascript·fetch·跨域·class
zhang98800003 小时前
JavaScript 核心原理深度解析-不停留于表面的VUE等的使用!
开发语言·javascript·vue.js
硅的褶皱4 小时前
对比分析LinkedBlockingQueue和SynchronousQueue
java·并发编程
MoFe14 小时前
【.net core】天地图坐标转换为高德地图坐标(WGS84 坐标转 GCJ02 坐标)
java·前端·.netcore
季鸢5 小时前
Java设计模式之观察者模式详解
java·观察者模式·设计模式
Fanxt_Ja5 小时前
【JVM】三色标记法原理
java·开发语言·jvm·算法
蓝婷儿5 小时前
6个月Python学习计划 Day 15 - 函数式编程、高阶函数、生成器/迭代器
开发语言·python·学习
love530love5 小时前
【笔记】在 MSYS2(MINGW64)中正确安装 Rust
运维·开发语言·人工智能·windows·笔记·python·rust
Mr Aokey6 小时前
Spring MVC参数绑定终极手册:单&多参/对象/集合/JSON/文件上传精讲
java·后端·spring