目录
序言:
1.生产环境中为了前后端更加友好的交互,我们需要对异常做标准化处理,向前端返回约定好的的状态码和异常信息
2.生产环境中为了更快的定位到程序异常,解决问题,我们需要对异常进行处理,处理成符合我们接受习惯的,更加友好的异常日志信息。
1.环境:
版本:spring boot:2.7.15
jdk:1.8
2.全局异常处理:
全局异常处理springboot项目给我们提供了增强类@RestControllerAdvice供我们使用
2.1配置全局捕获异常
异常捕获通过注解@ExceptionHandler
package com.iterge.iterge_pre.config;
import com.iterge.iterge_pre.entity.Response;
import com.iterge.iterge_pre.exception.BizException;
import com.iterge.iterge_pre.exception.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
/**
* @author liuph
* @date 2023/9/27 15:37:21
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionConfig {
@ExceptionHandler(Throwable.class)
public Response<Void> handleException(Throwable e, HttpServletRequest request) {
log.error("执行异常,异常信息:接口:{},信息:{}",request.getRequestURI(),e.getMessage());
return Response.of(new BizException(ErrorCode.UNKNOWN_EXCEPTION, e.getMessage()));
}
//自定义异常
@ExceptionHandler(BizException.class)
public Response<Void> bizHandleException(BizException e, HttpServletRequest request) {
log.error("执行异常,异常信息:接口:{},信息:{}",request.getRequestURI(),e.getMsg());
return Response.of(e);
}
}
2.2创建统一返回类、异常码
package com.iterge.iterge_pre.entity;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.iterge.iterge_pre.exception.BizException;
import lombok.Data;
/**
* @author liuph
* @date 2023/9/27 15:34:20
*/
@Data
public class Response<T> {
/**
* 成功返回ok, 异常返回错误原因。
*/
/**
* 返回的协议版本号
*/
private int version = 0;
/**
* 返回的状态码
*/
private int status = 0;
/**
* 错误信息
*/
@JsonProperty("err_msg")
@JsonAlias("errMsg")
private String errMsg = "";
/**
* 错误信息
*/
@JsonProperty("error_msg")
@JsonAlias("errorMsg")
private String errorMsg = "";
/**
* 服务器时间戳
*/
private long ts;
/**
* 返回的数据
*/
private T data;
public Response() {
ts = System.currentTimeMillis();
}
public static <T> Response<T> ok() {
return ok(null);
}
public static <T> Response<T> ok(T data) {
return ok(0, data);
}
public static <T> Response<T> ok(int version, T data) {
return ok(version, System.currentTimeMillis(), data);
}
public static <T> Response<T> ok(int version, long ts, T data) {
return ok(0, version, ts, "ok", data);
}
public static <T> Response<T> ok(int status, int version, long ts, String msg, T data) {
Response<T> resp = new Response<>();
resp.setData(data);
if (null != msg)
resp.setErrMsg(msg);
resp.setVersion(version);
resp.setTs(ts);
return resp;
}
public static <T> Response<T> of(BizException e) {
Response<T> response = new Response<>();
response.setStatus(e.getCode());
response.setErrMsg(e.getMsg());
response.setErrorMsg(e.getMessage());
response.setData(null);
return response;
}
}
package com.iterge.iterge_pre.exception;
/**
* @author liuph
* @date 2023/9/27 15:43:37
*/
public class ErrorCode {
public static final ErrorCode OK = define(0, "ok");
public static final ErrorCode PARAM_INVALID = define(-1, "参数错误");
public static final ErrorCode DB_EXCEPTION = define(-2, "数据库错误");
public static final ErrorCode REDIS_EXCEPTION = define(-3, "Redis错误");
public static final ErrorCode MEMCACHE_EXCEPTION = define(-4, "Memcache错误");
public static final ErrorCode MQ_EXCEPTION = define(-5, "MQ错误");
public static final ErrorCode RPC_EXCEPTION = define(-6, "RPC调用错误");
public static final ErrorCode CONFIG_EXCEPTION = define(-7, "配置错误");
public static final ErrorCode DATA_NOT_EXIST = define(-20, "数据不存在");
public static final ErrorCode AUTHENTICATION_FAILED = define(-41, "认证失败");
public static final ErrorCode PERMISSION_DENIED = define(-42, "权限不足");
public static final ErrorCode BUSINESS_EXCEPTION = define(-50, "业务异常");
public static final ErrorCode UNKNOWN_EXCEPTION = define(-100, "未知异常");
private int code;
private String msg;
protected ErrorCode() {
this.code = 0;
this.msg = null;
}
private ErrorCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
public static ErrorCode define(int code, String msg) {
return new ErrorCode(code, msg);
}
public static ErrorCode define(int code) {
return define(code, (String)null);
}
public int getCode() {
return this.code;
}
public String getMsg() {
return this.msg;
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
ErrorCode errorCode = (ErrorCode)o;
return this.code == errorCode.code;
} else {
return false;
}
}
}
2.3创建自定义异常类
package com.iterge.iterge_pre.exception;
/**
* @author liuph
* @date 2023/9/27 15:43:37
*/
public class ErrorCode {
public static final ErrorCode OK = define(0, "ok");
public static final ErrorCode PARAM_INVALID = define(-1, "参数错误");
public static final ErrorCode DB_EXCEPTION = define(-2, "数据库错误");
public static final ErrorCode REDIS_EXCEPTION = define(-3, "Redis错误");
public static final ErrorCode MEMCACHE_EXCEPTION = define(-4, "Memcache错误");
public static final ErrorCode MQ_EXCEPTION = define(-5, "MQ错误");
public static final ErrorCode RPC_EXCEPTION = define(-6, "RPC调用错误");
public static final ErrorCode CONFIG_EXCEPTION = define(-7, "配置错误");
public static final ErrorCode DATA_NOT_EXIST = define(-20, "数据不存在");
public static final ErrorCode AUTHENTICATION_FAILED = define(-41, "认证失败");
public static final ErrorCode PERMISSION_DENIED = define(-42, "权限不足");
public static final ErrorCode BUSINESS_EXCEPTION = define(-50, "业务异常");
public static final ErrorCode UNKNOWN_EXCEPTION = define(-100, "未知异常");
private int code;
private String msg;
protected ErrorCode() {
this.code = 0;
this.msg = null;
}
private ErrorCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
public static ErrorCode define(int code, String msg) {
return new ErrorCode(code, msg);
}
public static ErrorCode define(int code) {
return define(code, (String)null);
}
public int getCode() {
return this.code;
}
public String getMsg() {
return this.msg;
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
ErrorCode errorCode = (ErrorCode)o;
return this.code == errorCode.code;
} else {
return false;
}
}
}
2.4测试
controller中添加测试方式
@GetMapping("test1/{id}")
public Response<UInfo> getUser(@PathVariable("id") Integer id){
if(id != 1){
throw new BizException(ErrorCode.PARAM_INVALID);
}
log.info("成功");
return Response.ok();
}
以上代码的逻辑是:当我们的入参id!=1的时候抛出我们的自定义异常BizException,并通过全局异常类GlobalExceptionConfig捕获类进行捕获,捕获异常后,进行对程序员比较友好的日志打印,最后通过统一返回类Response进行接口结果返回
2.5扩展
如果业务上有别的异常需要单独处理,则可以在GlobalExceptionConfig类中使@ExceptionHandler注解进行新增。例如下面的空指针异常
//空指针异常
@ExceptionHandler(NullPointerException.class)
public Response<Void> nullPointerHandleException(NullPointerException e, HttpServletRequest request) {
log.error("空指针异常:接口:{},信息:{}",request.getRequestURI(),e.getMessage());
return Response.of(new BizException(ErrorCode.BUSINESS_EXCEPTION));
}