SpringBoot全局异常处理
1.前言
虽然SpringBoot已经提供了异常处理机制,但是这种方式不够灵活。可以根据其异常处理机制进行优化,自定义全局异常处理。
2.实战演练
2.1环境准备
SpringBoot版本:2.3.4.RELEASE
pom.xml需要的依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.66</version> </dependency>
2.2代码实现
1)首先定义一个基础的接口类,该接口由自定义的错误描述枚举类实现
/*** * 错误信息的基类接口 */ public interface BaseErrorInfoInterface { /** * 错误码 */ String getResultCode(); /** * 错误描述 */ String getResultMsg(); }
2)自定义一个枚举类,枚举可能出现的异常信息
/** * 枚举类 */ @AllArgsConstructor public enum CommonEnum implements BaseErrorInfoInterface { // 数据操作错误定义 SUCCESS("200", "成功!"), BODY_NOT_MATCH("400", "请求的数据格式不符!"), SIGNATURE_NOT_MATCH("401", "请求的数字签名不匹配!"), NOT_FOUND("404", "未找到该资源!"), INTERNAL_SERVER_ERROR("500", "服务器内部错误!"), SERVER_BUSY("503", "服务器正忙,请稍后再试!"); /** * 错误码 */ private String resultCode; /** * 错误描述 */ private String resultMsg; /** * 重写接口的方法,返回信息 * * @return */ @Override public String getResultCode() { return resultCode; } @Override public String getResultMsg() { return resultMsg; } }
3)自定义一个异常类,用于处理在业务操作过程中可能出现的异常
/** * 自定义异常类 */ @Setter @Getter @AllArgsConstructor @NoArgsConstructor public class MyException extends RuntimeException{ /** * 错误码 */ protected String errorCode; /** * 错误信息 */ protected String errorMsg; public MyException(String errorMsg) { super(errorMsg); this.errorMsg = errorMsg; } public MyException(String errorCode, String errorMsg, Throwable cause) { super(errorCode, cause); this.errorCode = errorCode; this.errorMsg = errorMsg; } @Override public synchronized Throwable fillInStackTrace() { return this; } }
4)为了数据统一格式返回,自定义一个数据的返回类
@Data @NoArgsConstructor @AllArgsConstructor public class ResultBody { /** * 响应代码 */ private String code; /** * 响应消息 */ private String message; /** * 响应结果 */ private Object result; public ResultBody(BaseErrorInfoInterface errorInfo) { this.code = errorInfo.getResultCode(); this.message = errorInfo.getResultMsg(); } /** * 成功 * * @return */ public static ResultBody success() { return success(null); } /** * 成功 * * @param data * @return */ public static ResultBody success(Object data) { ResultBody rb = new ResultBody(); rb.setCode(CommonEnum.SUCCESS.getResultCode()); rb.setMessage(CommonEnum.SUCCESS.getResultMsg()); rb.setResult(data); return rb; } /** * 失败 */ public static ResultBody error(BaseErrorInfoInterface errorInfo) { ResultBody rb = new ResultBody(); rb.setCode(errorInfo.getResultCode()); rb.setMessage(errorInfo.getResultMsg()); rb.setResult(null); return rb; } /** * 失败 */ public static ResultBody error(String code, String message) { ResultBody rb = new ResultBody(); rb.setCode(code); rb.setMessage(message); rb.setResult(null); return rb; } /** * 失败 */ public static ResultBody error(String message) { ResultBody rb = new ResultBody(); rb.setCode("-1"); rb.setMessage(message); rb.setResult(null); return rb; } @Override public String toString() { return JSONObject.toJSONString(this); } }
5)自定义全局异常处理类,用于捕获并处理所有的异常
@ControllerAdvice @Slf4j @RestController public class GlobalExceptionHandler { /** * 处理自定义的业务异常 * * @param e * @return */ @ExceptionHandler(value = MyException.class) public ResultBody bizExceptionHandler(MyException e) { log.error("发生业务异常!原因是:{}", e.getErrorMsg()); return ResultBody.error(e.getErrorCode(), e.getErrorMsg()); } /** * 处理空指针的异常 * * @param e * @return */ @ExceptionHandler(value = NullPointerException.class) public ResultBody exceptionHandler(NullPointerException e) { log.error("发生空指针异常!原因是:", e); return ResultBody.error(CommonEnum.BODY_NOT_MATCH); } /** * 处理其他异常 * * @param e * @return */ @ExceptionHandler(value = Exception.class) public ResultBody exceptionHandler(Exception e) { log.error("未知异常!原因是:", e); return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR); } }
6)新建一个controller即可,用于测试
@RestController @RequestMapping("/test") public class TestController { @GetMapping("/get") public String test() { int i = 10 / 0; return "hello"; } @GetMapping("/get2") public String test2(String str) { return str.toLowerCase(); } @PostMapping("add") public String test3(@RequestBody JSONObject json) { if (json.get("name") == null) { throw new MyException("-1", "姓名不能为空"); } return json.get("name") + " ****"; } }
7)使用postman分别对三个接口进行访问
访问localhost:8080/test/get,返回结果如下图:
访问localhost:8080/test/get2,不传递参数,如下图:
访问localhost:8080/test/get2?str=123,如下图:
访问localhost:8080/test/add,name的值是null,如下图:
访问localhost:8080/test/add,name的值不为null,如下图:
3.获取异常信息
一般情况下都是需要将异常信息记录下来,便宜运维,而若只使用e.getMessage()只能获取到异常的错误信息,却不知道异常发生在那里。下面通过两种不同的方法获取异常的完整信息
import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringWriter; /** * 异常工具类 */ public class ExceptionUtil { /** * 获取完整的异常信息 * * @param ex * @return */ public static String getAllExceptionInformation(Exception ex) { StringWriter stringWriter = new StringWriter(); PrintWriter writer = new PrintWriter(stringWriter); ex.printStackTrace(writer); StringBuffer buffer = stringWriter.getBuffer(); return buffer.toString(); } /** * 获取完整的异常信息 * * @param ex * @return */ public static String getAllExceptionInformation2(Exception ex) { ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream pout = new PrintStream(out); ex.printStackTrace(pout); String ret = new String(out.toByteArray()); pout.close(); try { out.close(); } catch (Exception e) { } return ret; } }
在业务中可直接调用,通过对比发现只是通过不同的方法将异常信息打印到流中。
就是这么简单,你学废了吗?感觉有用的话,给笔者点个赞吧 !