SpringBoot异常处理
异常的处理方式有多种:
- 自定义错误页面
-
@ExceptionHandler注解
-
@ControllerAdvice+@ExceptionHandler注解
-
配置SimpleMappingExceptionResolver处理异常
-
自定义 HandlerExceptionResolver 类处理异常
一、自定义错误页面
如果我们需要将所有的异常同一跳转到自定义的错误页面,需要在src/main/resources/templates(使用模板引擎时采用)或WEB-INF/jsp(与application.properties配置的视图映射一致)下创建自己的异常页面,名称必须是error。
(1) application.properties全局配置
#jsp视图映射配置 spring.mvc.view.prefix=/WEB-INF/jsp/ spring.mvc.view.suffix=.jsp
(2) WEB-INF/jsp/error.jsp内容
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ page isErrorPage="true"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>error页面</title> </head> <body> <h5>出错了,联系管理员......</h5> <p><%=exception.getMessage()%></p> </body> </html>
(3) 编写controller
@RestController public class DemoController { @RequestMapping("/showPage1") public Object showPage(){ ModelAndView view = new ModelAndView(); String demo = null; demo.toString(); //模拟异常 view.setViewName("error"); return view; } }
二、@ExceptionHandler注解
将指定的异常交由具体的方法处理,显示特定的页面。
1. 由@ExceptionHandler注解指定特定异常的处理
@RestController public class DemoController { /** * 模拟java.lang.NullPointerException异常 */ @RequestMapping("/showPage1") public Object showPage() { ModelAndView view = new ModelAndView(); String demo = null; demo.toString(); view.setViewName("index"); return view; } /** * java.lang.NullPointerException 处理空指针异常 * 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视 图的指定 * 参数 Exception e:会将产生异常对象注入到方法中 */ @ExceptionHandler(value = { java.lang.NullPointerException.class }) public ModelAndView nullPointerExceptionHandler(Exception e) { ModelAndView mv = new ModelAndView(); mv.addObject("error", e.toString()); mv.setViewName("error2"); return mv; } }
2. 编写error2.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ page isErrorPage="true"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>error页面</title> </head> <body> <h5>出错了,联系管理员......@ExceptionHandler</h5> <p>${error }</p> </body> </html>
三、@ControllerAdvice+@ExceptionHandler注解
仅使用@ExceptionHandler处理异常,需要主方法与异常处理方法在同一个Controller,这样异常处理的方法不能被其他Controller使用,有代码冗余。
使用@ControllerAdvice就可以解决这个问题。
1. 模拟异常的主方法
@RestController public class DemoController { /** * 模拟java.lang.NullPointerException异常 */ @RequestMapping("/showPage1") public Object showPage() { ModelAndView view = new ModelAndView(); String demo = null; demo.toString(); view.setViewName("index"); return view; } }
2. 创建异常处理的Controller
/** * @RestControllerAdvice或@ControllerAdvice */ @RestControllerAdvice public class ExceptionController { /** * java.lang.NullPointerException 处理空指针异常 * 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视 图的指定 * 参数 Exception e:会将产生异常对象注入到方法中 */ @ExceptionHandler(value = { java.lang.NullPointerException.class }) public ModelAndView nullPointerExceptionHandler(Exception e) { ModelAndView mv = new ModelAndView(); mv.addObject("error", e.toString()); mv.setViewName("error3"); return mv; } }
3. 编写jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ page isErrorPage="true"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>error页面</title> </head> <body> <h5>出错了,联系管理员......@ControllerAdvice</h5> <p>${error }</p> </body> </html>
四、配置SimpleMappingExceptionResolver处理异常
这种方法只能是对异常和视图进行关联,无法传递错误信息
//声明成一个配置类 @Configuration public class GlobalException { /** * 该方法必须要有返回值。返回值类型必须是: SimpleMappingExceptionResolver **/ @Bean public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver() { SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties mappings = new Properties(); /** * 参数一:异常的类型,注意必须是异常类型的全名 * 参数二:视图名称 **/ mappings.put("java.lang.ArithmeticException", "error1"); mappings.put("java.lang.NullPointerException", "error4"); // 设置异常与视图映射信息的 resolver.setExceptionMappings(mappings); return resolver; } }
五、自定义 HandlerExceptionResolver 类处理异常
创建一个实现现 HandlerExceptionResolver 接口的全局异常处理类
//声明成一个配置类 @Configuration public class GlobalException implements HandlerExceptionResolver{ @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView view = new ModelAndView(); //判断不同异常类型,做不同视图跳转 if(ex instanceof NullPointerException){ view.setViewName("error2"); view.addObject("error", ex.toString()); } return view; } }
六、以JSON字符返回的方式统一处理异常
前面几种异常处理都是返回到相应的错误页面,平常我们用到的大部分还是用JSON字符串返回(ajax提交的方式)。
第一种:使用@ControllerAdvice和@ExceptionHandler注解
第二种: 使用ErrorController类来实现。
以JSON格式返回统一对象:
/** * 响应对象 * @param <T> */ public class ResponseVo<T> implements Serializable { private static final long serialVersionUID = 1L; public static final int RESULT_FAIL = 0; public static final int RESULT_SUCCESS = 1; /** * 状态码. */ private Integer code; /** * 提示信息. */ private String msg; /** * 具体的数据. */ private T data; public ResponseVo() { } public ResponseVo(Integer code, String msg) { this.code = code; this.msg = msg; } public ResponseVo(Integer code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } @Override public String toString() { return "ResponseVo{" + "code=" + code + ", msg='" + msg + '\'' + ", data=" + data + '}'; } }
1. 使用@RestControllerAdvice和@ExceptionHandler注解
/** * @RestControllerAdvice 方法的返回值以字符串返回, * 等同于@ControllerAdvice + 方法上添加@ResponseBody */ @RestControllerAdvice public class GlobalExceptionHandler { /** * java.lang.NullPointerException 处理空指针异常 * 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视 图的指定 * 参数 Exception e:会将产生异常对象注入到方法中 */ @ExceptionHandler(value = { java.lang.NullPointerException.class }) public ResponseVo<String> nullPointerExceptionHandler(HttpServletResponse response, NullPointerException ex) { //这里最好也是判断下ex.getMessage()的信息是否为空(或"null"),如果是这样的话,使用ex.toString()将完整的错误信息展示出来 ResponseVo<String> responseVo = new ResponseVo<String>(ResponseVo.RESULT_FAIL, ex.getMessage()); return responseVo; } }
注解@RestControllerAdvice表示这是一个控制器增强类,当控制器发生异常且符合类中定义的拦截异常类,将会被拦截。可以使用basePackages属性定义拦截的控制器所在的包路径。
2. 使用ErrorController类来实现。
SpringBoot默认的错误处理类为BasicErrorController,这里编写一个自己的错误处理类,那么默认的处理类将不会起作用。
其中getErrorPath()返回的路径服务器将会重定向到该路径对应的处理类,本例中为error方法。
/** * @RestController直接以字符串的形式返回 * 等同于@Controller + 方法上添加@ResponseBody */ @RestController public class HttpErrorController implements ErrorController { private final static String ERROR_PATH = "/error"; @RequestMapping(path = ERROR_PATH) public ResponseVo<String> error(HttpServletRequest request, HttpServletResponse response) { ResponseVo<String> result = new ResponseVo<String>(ResponseVo.RESULT_FAIL, "HttpErrorController error:" + response.getStatus()); return result; } @Override public String getErrorPath() { return ERROR_PATH; } }
3. 测试
@RestController public class DemoController { @RequestMapping("/showPage") public Object showPage(){ throw new NullPointerException("DemoController have exception"); } }
(1) 发出一个错误的请求,也就是没有对应的处理类。从返回可以看到是由HttpErrorController类处理
{"code":0,"msg":"HttpErrorController error:404","data":null}
(2) 发出一个正常的请求(DemoController的showPage()处理),处理类中抛出空异样
{"code":0,"msg":"TestController have exception","data":null}
4. 两者的区别
(1) 注解@RestControllerAdvice方式只能处理控制器抛出的异常。此时请求已经进入控制器中。
(2) ErrorController方式可以处理所有的异常,包括未进入控制器的错误,比如404,401等错误
(3) 如果应用中两者共同存在,则@RestControllerAdvice方式处理控制器抛出的异常,类ErrorController方式未进入控制器的异常。
(4) @RestControllerAdvice方式可以定义多个拦截方法,拦截不同的异常类,并且可以获取抛出的异常信息,自由度更大。
5. 总结
(1) 因为返回的是JSON字符串,所以可以使用@RestXXXX格式或@XXXX+@ResponseBody的组合
(2) ErrorController的处理范围比第一种处理方式要大,如果两种异常处理的方式在一个web应用同时存在,需要确保第一种处理方式是返回JSON字符串,否则可能会被ErrorController再一次处理。