对于业务异常,不知道大家都怎么处理,写异常我们第一能快速发现异常,第二异常有足够的信息能让我们快速定位问题
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 这层,如何实现通用的一些设计,主要包含了通用返回值及统一异常处理的设计。