对于后端程序员来说,写Java程序的时候, 处理异常是必须要做的事,也是一个系统最重要的环节,当一个项目变得很大的时候,异常处理和日志系统能让你快速定位到问题。对于用户或者接口调用者而言,优雅的异常处理可以让调用者快速知道问题所在。本文将介绍如何优雅地处理异常。
枚举类,方便统一管理异常CODE和异常信息
/**
* @author : 小影
* @date : 2021/1/20 17:45
* @description:
*/
public enum ErrCodeEnum {
// 请求方式错误,请使用GET|POST
unknown_405(405, "Request mode error"),
// 内部服务器错误
unknown_500(500, "Internal server error"),
// 未知异常,请联系技术工程师
unknown_9999(9999, "If the fault is unknown, contact technical support"),
// 网络异常,稍候重试
unknown_5555(5555, "Network exception, please try again later"),
// 请求参数异常
unknown_10000(10000, "Request parameter exception"),
// 请求正文丢失
unknown_10001(10001, "Required request body is missing"),
unknown_10002(10002, "待拓展"),
unknown_10003(10003, "待拓展"),
unknown_10004(10004, "待拓展"),
unknown_10005(10005, "待拓展"),
unknown_10006(10006, "待拓展"),
unknown_10007(10007, "待拓展"),
unknown_10008(10008, "待拓展"),
unknown_10009(10009, "待拓展"),
unknown_10010(10010, "待拓展"),
unknown_10011(10011, "待拓展"),
unknown_10012(10012, "待拓展"),
unknown_10013(10013, "待拓展"),
unknown_10014(10014, "待拓展"),
unknown_10015(10015, "待拓展"),
unknown_10016(10016, "待拓展"),
unknown_10017(10017, "待拓展"),
unknown_10018(10018, "待拓展"),
unknown_10019(10019, "待拓展"),
unknown_10020(10020, "待拓展"),
unknown_10021(10021, "待拓展"),
unknown_10022(10022, "待拓展"),
unknown_10023(10023, "待拓展"),
unknown_10024(10024, "待拓展"),
unknown_10025(10025, "待拓展");
private Integer code;
private String message;
ErrCodeEnum(Integer code,
String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "{" +
"code:" + code +
", message:'" + message + '\'' +
'}';
}
}
优雅的 Assert(断言) 可以用来校验业务的异常情况,消除 if else,将判断方法封装,直接使用.方法名的方法,把会出现的业务异常尽可能的封装到一个类中。简化控制层。方便以后扩展用
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* @author 小影
* @create 2022-04-24 9:24
* @describe:断言
*/
public abstract class Assert {
/**
* 如果条件为true就抛异常
* @param expression
* @param message
*/
public static void isTrue(boolean expression, ErrCodeEnum message) {
if (expression) {
throw new IllegalArgumentException(message.toString());
}
}
/**
* 小于等于0
* @param number
* @param message
*/
public static void isGreater(int number, ErrCodeEnum message) {
if (number <= 0) {
throw new IllegalArgumentException(message.toString());
}
}
/**
* 空对象
* @param obj
* @param message
*/
public static void isEmpty(Object obj, ErrCodeEnum message) {
if (ObjectUtils.isEmpty(obj)) {
throw new IllegalArgumentException(message.toString());
}
}
/**
* 空字符
* @param message
* @param text
*/
public static void isStr(ErrCodeEnum message, @Nullable String... text) {
for (String s : text) {
if (!StringUtils.hasText(s)) {
throw new IllegalArgumentException(message.toString());
}
}
}
/**
* 字符串是否相等
* @param str1
* @param str2
* @param message
*/
public static void isStrEq(String str1, String str2, ErrCodeEnum message) {
if (!str1.equals(str2)) {
throw new IllegalArgumentException(message.toString());
}
}
/**
* 如果条件为false 就抛异常
* @param expression
* @param message
*/
public static void isFalse(boolean expression, ErrCodeEnum message) {
if (!expression) {
throw new IllegalArgumentException(message.toString());
}
}
}
将响应给用户的所有的错误都以Json的方式返回,因此封装一个通用的返回体
import lombok.Data;
@Data
public class ResultUtils {
//状态码:200成功 100失败
private int code;
//成功或失败的提示信息
private String msg;
//用户要返回给浏览器的数据
private T data;
//成功调用的方法
public static ResultUtils success(){
ResultUtils mag = new ResultUtils();
mag.setCode(200);
mag.setMsg("成功!");
return mag;
}
//成功调用的方法
public static ResultUtils success(T data) {
ResultUtils entity = new ResultUtils();
entity.setData(data);
entity.setMsg("成功!");
entity.setCode(200);
return entity;
}
//失败调用的方法
public static ResultUtils error(ErrCodeEnum errCodeEnum) {
ResultUtils entity = new ResultUtils();
entity.setCode(errCodeEnum.getCode());
entity.setMsg(errCodeEnum.getMessage());
return entity;
}
//失败调用的方法
public static ResultUtils error(ErrCodeEnum errCodeEnum,String msg) {
ResultUtils entity = new ResultUtils();
entity.setCode(errCodeEnum.getCode());
entity.setMsg(msg);
return entity;
}
//失败调用的方法
public static ResultUtils error(Integer code, String message) {
ResultUtils entity = new ResultUtils();
entity.setCode(code);
entity.setMsg(message);
return entity;
}
//失败调用的方法
public static ResultUtils error(ErrCodeEnum errCodeEnum,String msg,T obj) {
ResultUtils entity = new ResultUtils();
entity.setCode(errCodeEnum.getCode());
entity.setMsg(msg);
entity.setData(obj);
return entity;
}
//失败调用的方法
public static ResultUtils error(ErrCodeEnum errCodeEnum,T obj) {
ResultUtils entity = new ResultUtils();
entity.setCode(errCodeEnum.getCode());
entity.setData(obj);
return entity;
}
}
将异常进行统一处理,减少try-catch模板代码,减少编码量,提升扩展性和可维护性。
import com.alibaba.fastjson.JSONObject;
import com.ying.demo.utils.ErrCodeEnum;
import com.ying.demo.utils.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
/**
* @author : 小影
* @date : 2021/1/20 17:45
* @description:
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 请求体丢失
* @param request
* @param re
* @return
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.OK)
public ResultUtils httpMessageNotReadableException(HttpServletRequest request, HttpMessageNotReadableException re) {
log.error("[GlobalExceptionHandler]:"+re.getMessage());
return ResultUtils.error(ErrCodeEnum.unknown_10001, "Required request body is missing");
}
/**
* JSR303 校验拦截
* 如果抛出的是参数异常,将其当做业务异常处理
* 参数校验不通过时,抛出以下异常
* @param request
* @param re
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.OK)
public ResultUtils handlerMethodArgumentNotValid(HttpServletRequest request, MethodArgumentNotValidException re) {
StringBuilder sb = new StringBuilder();
re.getBindingResult().getAllErrors().forEach(error -> {
String fieldName = ((FieldError) error).getField();
sb.append("[").append(fieldName).append("] ").append(error.getDefaultMessage());
});
log.info("Parameter check problem:{}",sb);
return ResultUtils.error(ErrCodeEnum.unknown_10000, sb.toString());
}
/**
* 请求方式错误拦截
* @param request
* @param e
* @return
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseStatus(HttpStatus.OK)
public ResultUtils handlerHttpRequestMethodNotSupportedException(HttpServletRequest request, HttpRequestMethodNotSupportedException e) {
return ResultUtils.error(ErrCodeEnum.unknown_405,e.getMessage());
}
/**
* 断言异常拦截
* @param request
* @param e
* @return
*/
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.OK)
public ResultUtils illegalArgumentException(HttpServletRequest request, IllegalArgumentException e) {
log.error("Assertion Vs Exception:{}", e.getMessage());
JSONObject error = JSONObject.parseObject(e.getMessage());
return ResultUtils.error(error.getInteger("code"), error.getString("message"));
}
/**
* 其他异常拦截
* @param e
* @param request
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResultUtils handleException(Exception e, HttpServletRequest request) {
log.error("Global Exception Occured => url : {}, msg : {}", request.getRequestURL(), e.getMessage());
e.printStackTrace();
return ResultUtils.error(ErrCodeEnum.unknown_500);
}
}