Spring MVC 之 异常处理

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

异常处理

简单异常处理器

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

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

案例演示

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

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 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页面分别如下:(内容修改成指定的页面)

<%@ 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:

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()方法的具体代码如下所示

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

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

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执行时抛出的系统异常。

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;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路卿老师

大哥大姐给点吧!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值