一、异常基础
异常分为两类:
① 未到达Controller,拦截器异常、404等
② 到达Controller,系统异常空指针等500异常
二、列举springBoot中常用的五种统一处理异常方式
-
自定义错误页面
springboot默认处理异常是当出现异常的时候,会找到内置的 /error 路径,返回错误信息。那么我们重写error.html页面,或者建立/resource/error目录,添加404.html,500.html静态页面。
目录结构
这里需要注意一点,名字必须一致,建目录时名字必须为/error,或者error.html。
缺点:拦截所有异常,不灵活 -
@ExceptionHandle 注解处理异常
在Controller类中添加拦截异常方法并使用 @ExceptionHandler 注解@RestController public class ExceptionHandler2 { @RequestMapping("/handler2/exception") public String index() { String s = null; s.length(); return "hello world!"; } /** * 用于拦截本类的java.lang.NullPointerException异常 * 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视图的指定 * 参数 Exception e:会将产生异常对象注入到方法中 */ @ExceptionHandler(value = NullPointerException.class) public ModelAndView nullExceptionHanlder(Exception e) { ModelAndView mv = new ModelAndView(); mv.addObject("error", "ExceptionHandler2: "+e.toString()); mv.setViewName("/handler/error"); return mv; } }
缺点:只对本类起作用,针对每种类型异常都要编码,代码冗余,工作量大。
-
@ControllerAdvice/@RestControllerAdvice+@ExceptionHandler 注解处理异常
首先新建统一处理类,GlobalException.java@RestControllerAdvice(basePackageClasses={ExceptionHandler3.class}) public class GlobalException { private static String errorPath = "/handler/error"; /** * 用于拦截本类的java.lang.NullPointerException异常 * 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视图的指定 * 参数 Exception e:会将产生异常对象注入到方法中 */ @ExceptionHandler(value = NullPointerException.class) public ModelAndView nullExceptionHanlder(Exception e) { ModelAndView mv = new ModelAndView(); mv.addObject("error", "ExceptionHandler3: "+e.toString()); mv.setViewName(errorPath); return mv; } /** * 编写拦截其他异常的方法.... */ }
Controller.java
@RestController public class ExceptionHandler3 { @RequestMapping("/handler3/exception") public String index() { String s = null; s.length(); return "hello world!"; } }
-
@Configuration + 配置 SimpleMappingExceptionResolver 处理异常
GlobalException4Handler4.java@Configuration public class GlobalException4Handler4 { private static String errorPath = "/handler/error"; /** * 该方法必须要有返回值。返回值类型必须是: SimpleMappingExceptionResolver * @return */ @Bean public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver() { SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties properties = new Properties(); // 参数一:异常的类型,注意必须是异常类型的全名 参数二:视图名称 properties.put("java.lang.NullPointerException",errorPath); properties.put("java.lang.ArithmeticException",errorPath); // 添加其他异常类型... // 设置异常与视图映射信息的 resolver.setExceptionMappings(properties); return resolver; } }
@RestController public class ExceptionHandler4 { @RequestMapping("/handler4/exception") public String index() { int i = 1/0; return "hello world!"; } }
缺点:没有异常信息!没有异常信息!!没有异常信息!!!
-
自定义 HandlerExceptionResolver 类处理异常
@Configuration public class GlobalException4Handler5 implements HandlerExceptionResolver { private static String errorPath = "/handler/error"; /** * 实现resolveException方法,判断异常类型进行跳转 * @param request * @param response * @param handler * @param ex * @return */ @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView mv = new ModelAndView(); //判断不同异常类型,做不同视图跳转 if(ex instanceof ArithmeticException){ mv.setViewName(errorPath); } if(ex instanceof NullPointerException){ mv.setViewName(errorPath); } // 添加其他异常类型... mv.addObject("error", "Exception5: "+ex.toString()); return mv; } }
@RestController public class ExceptionHandler5 { @RequestMapping("/handler5/exception") public String index() { int i = 1/0; return "hello world!"; } }
以上是springBoot处理异常的五种方式。
推荐使用3、5两种方式,进行统一管理。
三、开发中处理异常demo,统一返回JSON数据,统计异常信息
-
环境: springboot2.1.8、lombok
-
项目结构:
-
代码块
ExceptionEnum.java/** * 返回状态枚举类 * @author GchengLiu */ public enum ExceptionEnum { /** * 添加异常类型代码与信息枚举,可以多添加一些。 */ SUCCESS(0000, "请求成功"), FAIL(1000, "请求失败"), BODY_NOT_MATCH(1002,"请求的数据格式不符!"), NOT_FOUND(1004, "未找到该资源!"), INTERNAL_SERVER_ERROR(1005, "服务器内部错误!"), SERVER_BUSY(1006,"服务器正忙,请稍后再试!"); private Integer code; private String msg; ExceptionEnum(Integer code, String msg) { this.code = code; this.msg = msg; } 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; } }
GlobalExceptionHandler.java
@RestControllerAdvice @Slf4j public class GlobalExceptionHandler{ /** * 可以添加自定义异常拦截 */ // ------------------------------------------------------------------ /** * 404的拦截. * * @param request * @param ex * @return */ @ResponseStatus(code = HttpStatus.NOT_FOUND) @ExceptionHandler(NoHandlerFoundException.class) public Result<String> notFound(HttpServletRequest request, Exception ex) { log.error("request uri:{} ,not found exception:{}", request.getRequestURI(), ex); return ResponseUtil.noHandlerException(request.getRequestURI(),null); } /** * 拦截500错误 * @param req * @param ex * @return */ @ExceptionHandler public Result<String> nullExceptionHandler(HttpServletRequest req, Exception ex){ log.error("request uri:{} server exception:{}", req.getRequestURI(), ex); return ResponseUtil.serverException(req.getRequestURI()); } }
ResponseUtil.java
/** * 请求返回类 * @author GchengLiu */ public class ResponseUtil implements Serializable { public static <T> Result<T> builderResponse(int code, String msg, String uri, T data) { Result<T> res = new Result(); res.setCode(code); res.setResMsg(msg); res.setUri(uri); res.setData(data); return res; } /** * 添加常用方法 */ public static <T> Result<T> success(String msg,String uri) { return builderResponse(ExceptionEnum.SUCCESS.getCode(), msg, uri,null); } public static <T> Result<T> success(String msg,String uri, T data) { return builderResponse(ExceptionEnum.SUCCESS.getCode(), msg, uri, data); } public static <T> Result<T> success(String uri, T data) { return builderResponse(ExceptionEnum.SUCCESS.getCode(), ExceptionEnum.SUCCESS.getMsg(), uri, data); } public static <T> Result<T> success(String uri) { return builderResponse(ExceptionEnum.SUCCESS.getCode(), ExceptionEnum.SUCCESS.getMsg(), uri,null); } public static <T> Result<T> failure(String uri) { return builderResponse(ExceptionEnum.FAIL.getCode(), ExceptionEnum.FAIL.getMsg(), uri, null); } public static <T> Result<T> failure(String msg,String uri) { return builderResponse(ExceptionEnum.FAIL.getCode(), msg, uri, null); } public static <T> Result<T> failure(String uri, T date) { return builderResponse(ExceptionEnum.FAIL.getCode(), ExceptionEnum.FAIL.getMsg(),uri, date); } public static <T> Result<T> illegalRequest(String uri) { return builderResponse(ExceptionEnum.BODY_NOT_MATCH.getCode(), ExceptionEnum.BODY_NOT_MATCH.getMsg(),uri, null); } public static <T> Result<T> noHandlerException(String uri,T data) { return builderResponse(ExceptionEnum.NOT_FOUND.getCode(), ExceptionEnum.NOT_FOUND.getMsg(), uri, data); } public static <T> Result<T> serverException(String uri) { return builderResponse(ExceptionEnum.INTERNAL_SERVER_ERROR.getCode(), ExceptionEnum.INTERNAL_SERVER_ERROR.getMsg(),uri, null); } public static <T> Result<T> serverBusyException(String uri) { return builderResponse(ExceptionEnum.SERVER_BUSY.getCode(), ExceptionEnum.INTERNAL_SERVER_ERROR.getMsg(), uri,null); }
Result.java
/** * 请求返回数据格式 * @param <T> * @author GchengLiu */ @Data @NoArgsConstructor public class Result<T> { private Integer code; private String resMsg; private String uri; private T data; public String toJson() { return this.data == null ? JSON.toJSONString(this) : this.toJson(SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteMapNullValue); } public String toJson(SerializerFeature... features) { return features == null ? this.toJson() : JSON.toJSONString(this, features); } }
application.yml
server: port: 8888 debug: false spring: # json 输出格式化与时间格式化 jackson: date-format: yyyy-MM-dd HH:mm:ss SSS time-zone: GMT+8 serialization: indent-output: true # json 过滤掉返回为null的字段,用注解也可以 default-property-inclusion: non_null mvc: static-path-pattern: /static/** # 捕获404异常, throw-exception-if-no-handler-found: true
-
测试
404异常{ "code" : 1004, "resMsg" : "未找到该资源!", "uri" : "/1" }
500异常
{ "code" : 1005, "resMsg" : "服务器内部错误!", "uri" : "/index" }
好啦,统一处理异常方式就总结到这里。有问题不要吝啬留言啊~