SpringMVC之异常处理

103 阅读4分钟

对于业务异常,不知道大家都怎么处理,写异常我们第一能快速发现异常,第二异常有足够的信息能让我们快速定位问题

1、思路

采用 aop 的方式,将异常处理和业务代码进行分离,让框架拦截所有方法的执行,目标方法中不要在捕获异常了,直接将异常抛出去,由统一的地方进行进行处理。

此时异常处理和业务代码分离开了,没有耦合在一起了,此时如果需要调整异常的处理逻辑,会非常方便,只需要修改统一处理异常的代码,就 ok 了。

如果对 spring 的 aop 比较熟悉的,实现起来还是很容易的,只需要一个环绕拦截器就可以了,有兴趣的朋友可以去试试。

本文是 springmvc 的内容,而 springmvc 中提供了类似的方式来统一处理系统所有的异常,Controller 中的所有方法都无需捕获异常,只需要做一些配置,springmvc 框架就会自动捕获异常,对异常进行集中处理,下面咱们来看看这玩意是怎么玩的。

2、SpringMVC集中统一处理异常的使用步骤

2.1 需求

写个登录方法,方法中验证用户和密码,验证失败的时候分别抛出对应的异常,成功了跳转到 success.jsp 页面,代码如下。

@Controller
public class UserController {

    @RequestMapping("/login")
    public ModelAndView login(@RequestParam("name") String name,
                              @RequestParam("pass") Integer pass) throws Exception {
        //用户名必须为路人
        if (!"路人".equals(name)) {
            throw new NameException("用户名有误!");
        }
        //密码必须为666
        if (Integer.valueOf(666).equals(pass)) {
            throw new PassException("密码有误!");
        }
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("name", name);
        modelAndView.setViewName("success");
        return modelAndView;
    }
}

/**
 * 姓名异常类
 */
public class NameException extends Exception {
    public NameException(String message) {
        super(message);
    }
}

/**
 * 密码异常类
 */
public class PassException extends Exception {
    public PassException(String message) {
        super(message);
    }
}

2.2 分析

异常大体分为 3 类:NameException、PassException、其他异常,咱们需要实现让 springmvc 来统一处理这 3 类异常,然后将错误信息输出到错误页面。

2.3 创建全局异常处理类

这个步骤是重点,内部包含 3 个小的步骤。

第 1 步:创建一个普通的类,作为全局异常处理类

第 2 步:在类上添加@ControllerAdvice 注解,从注解的名称包含了 Advice 可以看出,aop 中我们接触过 Advice(通知),用来对 bean 的功能进行增强,而这个注解是对 Controller 的功能进行增强,用来集中处理 Controller 的所有异常。

第 3 步:添加处理异常的方法,方法上需要加上@ExceptionHandler 注解,这个注解有个 value 属性,用来指定匹配的异常类型,当 springmvc 捕获到控制器异常后,会和这个异常类型进行匹配,匹配成功了,将调用@ExceptionHandler 标注的方法;如果未指定 value 的值,表示匹配所有类型的异常。

最终代码如下,类中添加了 3 个方法,分别用来处理 3 类异常,方法的每一行输出了一条日志,稍后可以通过这个验证效果,方法内部对错误进行了封装,然后跳转到错误页面(error.jsp)进行展示,稍后我们会通过不同的场景来验证效果。

/**
 * 统一异常处理类
 */
@ControllerAdvice
public class GlobalExceptionHandle {

    /**
     * 此方法用来处理 NameException 类型的异常,
     * 当controller抛出NameException异常的时候,此方法会被调用
     *
     * @param e
     * @return
     */
    @ExceptionHandler({NameException.class})
    public ModelAndView doNameException(Exception e) {
        System.out.println("-----doNameException-----");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("error");
        modelAndView.addObject("msg", "登录名有误!");
        modelAndView.addObject("e", e);
        return modelAndView;
    }

    /**
     * 此方法用来处理 AgeException 类型的异常,
     * 当controller抛出NameException异常的时候,此方法会被调用
     *
     * @param e
     * @return
     */
    @ExceptionHandler({PassException.class})
    public ModelAndView doPassException(Exception e) {
        System.out.println("-----doPassException-----");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("error");
        modelAndView.addObject("msg", "密码有误!");
        modelAndView.addObject("e", e);
        return modelAndView;
    }

    /**
     * 此方法用来处理任意异常(也就是上面2个方法不能够处理的异常都会被这个方法处理)
     *
     * @param e
     * @return
     */
    @ExceptionHandler
    public ModelAndView doException(Exception e) {
        System.out.println("-----doException-----");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("error");
        modelAndView.addObject("msg", "系统异常!");
        modelAndView.addObject("e", e);
        return modelAndView;
    }
}

2.4 配置

<?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:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd   http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 设置controller扫描路径 -->
    <context:component-scan base-package="com.example.controller"/>
    <!-- 设置全局异常处理器扫描路径 -->
    <context:component-scan base-package="com.example.handle"/>
    <!-- 添加mvc注解驱动 -->
    <mvc:annotation-driven/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

2.5 创建页面

登录页面、成功页面、错误页面,具体见下面的代码

3、总结

  • 本文详细介绍了 springmvc 集中统一异常处理的具体用法,重点主要用到了 2 个注解@ControllerAdvice@ExceptionHandler

  • 目前多数系统都是前后端分离了,后端所有的接口都返回 json 格式的数据,所以下一篇文章,来一篇实战的文章,带大家看下在 controller 这层,如何实现通用的一些设计,主要包含了通用返回值及统一异常处理的设计。