实现全局异常处理的目的,是为了给用户一个友好的错误提示,并且不需要在Controller层去对Service层的异常做捕获,
去掉这些重复性的代码,具体实现如下:
package com.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* 全局异常处理,在这里捕获的是将要抛给 JVM 的异常,这些异常会返回给调用者,而我们不希望调用者看到这些数据,在这里屏蔽
*/
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@ExceptionHandler(Exception.class)
public ClientResult handleException(Exception e) {
logger.error("{}", BaseRetCode.ERR_SERVER_EXCEPTION, e);
return ClientResult.ERROR(BaseRetCode.ERR_SERVER_EXCEPTION);
}
@ExceptionHandler(LogicalException.class)
public ClientResult handleLogicalException(LogicalException e) {
// TODO 这里可以配置邮件或者短信提醒
logger.error(e.getMessage(), e);
return ClientResult.ERROR(e.errMsg, e.retCode);
}
/**
* 处理实体字段校验不通过异常
*
* @param e MethodArgumentNotValidException
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ClientResult handleValidException(MethodArgumentNotValidException e) {
BindingResult result = e.getBindingResult();
FieldError error = result.getFieldError();
logger.error(error.getDefaultMessage(), e);
return ClientResult.ERROR(BaseRetCode.ERR_BAD_PARAMS.retCode, error.getDefaultMessage());
}
}
ClientResult 是返回给客户端的统一格式:
package com.test;
import java.io.Serializable;
/**
* 客户端返回数据统一格式
*/
public class ClientResult<T> implements Serializable {
private static final long serialVersionUID = 6025839078803463799L;
// 错误码
private String retCode;
// 错误描述
private String message;
// 返回数据
private T result;
private boolean success = true;
private ClientResult() {
}
private ClientResult(BaseRetCode.RetCode retCode) {
this.retCode = retCode.retCode;
this.message = retCode.message;
if (retCode.equals(BaseRetCode.OK)) {
this.success = false;
} else {
this.success = false;
}
}
public String getRetCode() {
return retCode;
}
public ClientResult setRetCode(String retCode) {
this.retCode = retCode;
return this;
}
public ClientResult setMessage(String message) {
this.message = message;
return this;
}
public boolean isSuccess() {
return success;
}
public boolean isError() {
return !isSuccess();
}
public ClientResult setSuccess(boolean success) {
this.success = success;
return this;
}
public String getMessage() {
return message;
}
public T getResult() {
return result;
}
public ClientResult<T> setResult(T result) {
this.result = result;
return this;
}
/**
* 错误
* @param retCode 错误码
* @return ClientResult
*/
public static ClientResult ERROR(BaseRetCode.RetCode retCode) {
return new ClientResult(retCode);
}
public static ClientResult ERROR(String retCode, String message) {
ClientResult clientResult = new ClientResult();
clientResult.setRetCode(retCode)
.setMessage(message)
.setSuccess(false);
return clientResult;
}
/**
* 不支持该操作(BaseRetCode.ERR_OPERATION_NOT_SUPPORTED)
* @param message 错误描述
* @return ClientResult
*/
public static ClientResult Error(String message) {
ClientResult clientResult = new ClientResult(BaseRetCode.ERR_OPERATION_NOT_SUPPORTED);
clientResult.setMessage(message);
return clientResult;
}
public static<T> ClientResult<T> SUCCESS(T result) {
ClientResult<T> cr = new ClientResult<>(BaseRetCode.OK);
return cr.setResult(result);
}
public static ClientResult SUCCESS() {
return new ClientResult(BaseRetCode.OK);
}
}
LogicalException 类是一个业务逻辑异常,业务代码里面抛出 异常统一使用这个,方便以后做其他处理:
package com.test;
/**
* 逻辑错误异常
* <p>
* @author Demon-HY
*/
public class LogicalException extends RuntimeException {
private static final long serialVersionUID = 1901960469589537111L;
public String retCode;
public String errMsg;
public LogicalException(String retCode, String errMsg) {
super(errMsg);
this.retCode = retCode;
this.errMsg = errMsg;
}
public LogicalException(BaseRetCode.RetCode retCode) {
super(retCode.message);
this.retCode = retCode.retCode;
this.errMsg = retCode.message;
}
public LogicalException(BaseRetCode.RetCode retCode, Exception e) {
super(retCode.message, e);
this.retCode = retCode.retCode;
this.errMsg = retCode.message;
}
@Override
public String toString() {
return "LogicalException{" +
"retCode=" + retCode +
", errMsg='" + errMsg + '\'' +
'}';
}
}
BaseRetCode类是用来存放基础的错误描述,方便调用者根据返回错误做不同的处理:
package com.test;
import java.io.Serializable;
import java.util.Objects;
/**
* 系统错误码
*/
public class BaseRetCode implements Serializable {
private static final long serialVersionUID = 8351895774014913411L;
/**
* 操作成功
*/
public static final RetCode OK;
/**
* 系统繁忙,请稍后重试
*/
public static final RetCode ERR_SERVER_EXCEPTION;
/**
* 参数错误
*/
public static final RetCode ERR_BAD_PARAMS;
/**
* 无访问权限
*/
public static final RetCode ERR_FORBIDDEN;
/**
* 非法JSON串
*/
public static final RetCode ERR_INVALID_JSON;
/**
* 资源不存在
*/
public static final RetCode ERR_NOT_FOUND;
/**
* 不支持该操作
*/
public static final RetCode ERR_OPERATION_NOT_SUPPORTED;
static {
OK = add("操作成功", "OK");
ERR_SERVER_EXCEPTION = add("系统繁忙,请稍后重试", "ERR_SERVER_EXCEPTION");
ERR_BAD_PARAMS = add("参数错误", "ERR_BAD_PARAMS");
ERR_FORBIDDEN = add("无访问权限", "ERR_FORBIDDEN");
ERR_INVALID_JSON = add("非法JSON串", "ERR_INVALID_JSON");
ERR_NOT_FOUND = add("资源不存在", "ERR_NOT_FOUND");
ERR_OPERATION_NOT_SUPPORTED = add("不支持该操作", "ERR_OPERATION_NOT_SUPPORTED");
}
public static class RetCode implements Serializable {
private static final long serialVersionUID = -1L;
public String message;
public String retCode;
private RetCode(String message, String retCode) {
this.message = message;
this.retCode = retCode;
}
static RetCode newInstance(String message, String retCode) {
return new RetCode(message, retCode);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RetCode retCode1 = (RetCode) o;
return Objects.equals(retCode, retCode1.retCode);
}
@Override
public int hashCode() {
return Objects.hash(message, retCode);
}
@Override
public String toString() {
return "RetCode{" +
"message='" + message + '\'' +
", retCode='" + retCode + '\'' +
'}';
}
}
public static BaseRetCode.RetCode add(String message, String retCode) {
return RetCode.newInstance(message, retCode);
}
}
上面定义的错误码是一些基础性的错误, 具体业务相关的代码需要继承上面的类,比如实现一个用户错误码:
package com.test;
import java.io.Serializable;
/**
* 用户错误码
*/
public class UserRetCode extends BaseRetCode implements Serializable {
private static final long serialVersionUID = -1L;
/**
* 用户不存在
*/
public static final RetCode ERR_USER_NOT_FOUND;
/**
* 禁止修改账号信息
*/
public static final RetCode ERR_REVISE_ACCOUNT_FORBIDDEN;
/**
* 创建用户失败
*/
public static final RetCode ERR_CREATE_USER_FAILED;
/**
* 设置用户属性失败
*/
public static final RetCode ERR_SET_USER_ATTR_FAILED;
/**
* 删除用户失败
*/
public static final RetCode ERR_DELETE_USER_FAILED;
/**
* 该账号已被使用
*/
public static final RetCode ERR_ACCOUNT_EXIST;
/**
* 手机已被使用
*/
public static final RetCode ERR_PHONE_USED;
/**
* 邮箱已被使用
*/
public static final RetCode ERR_EMAIL_USED;
/**
* 无效电话号码
*/
public static final RetCode ERR_ILLEGAL_PHONE;
/**
* 无效密码
*/
public static final RetCode ERR_ILLEGAL_PASSWORD;
/**
* 用户信息损坏
*/
public static final RetCode ERR_USER_INFO_BROKEN;
/**
* 密码错误
*/
public static final RetCode ERR_WRONG_PASSWORD;
/**
* 设置密码失败
*/
public static final RetCode ERR_RESET_PASSWORD_FAILED;
/**
* 用户已被锁定
*/
public static final RetCode ERR_USER_LOCKED;
/**
* 找回密码邮件过期
*/
public static final RetCode ERR_RETRIEVE_EMAIL_EXPIRED;
/**
* 无效的用户名
*/
public static final RetCode ERR_ILLEGAL_USER_NAME;
/**
* 用户已被删除
*/
public static final RetCode ERR_USER_DELETE;
/**
* 用户名或密码不能为空
*/
public static final RetCode ERR_ACCOUNT_PWD_NULL;
/**
* 手机号已经被绑定
*/
public static final RetCode ERR_PHONE_ALREADY_BOUND;
/**
* 非法手机号
*/
public static final RetCode ERR_ILLEGAL_PHONE_ACCOUNT;
/**
* 非法账号类型
*/
public static final RetCode ERR_ILLEGAL_ACCOUNT_TYPE;
/**
* 非法邮箱
*/
public static final RetCode ERR_ILLEGAL_EMAIL_ACCOUNT;
static {
ERR_USER_NOT_FOUND = add("用户不存在", "ERR_USER_NOT_FOUND");
ERR_REVISE_ACCOUNT_FORBIDDEN = add("禁止修改账号信息", "ERR_REVISE_ACCOUNT_FORBIDDEN");
ERR_CREATE_USER_FAILED = add("创建用户失败", "ERR_CREATE_USER_FAILED");
ERR_SET_USER_ATTR_FAILED = add("设置用户属性失败", "ERR_SET_USER_ATTR_FAILED");
ERR_DELETE_USER_FAILED = add("删除用户失败", "ERR_DELETE_USER_FAILED");
ERR_ACCOUNT_EXIST = add("该账号已被使用", "ERR_ACCOUNT_EXIST");
ERR_PHONE_USED = add("手机已被使用", "ERR_PHONE_USED");
ERR_EMAIL_USED = add("邮箱已被使用", "ERR_EMAIL_USED");
ERR_ILLEGAL_PHONE = add("无效电话号码", "ERR_ILLEGAL_PHONE");
ERR_ILLEGAL_PASSWORD = add("无效的密码", "ERR_ILLEGAL_PASSWORD");
ERR_USER_INFO_BROKEN = add("用户信息损坏", "ERR_USER_INFO_BROKEN");
ERR_WRONG_PASSWORD = add("用户名或密码错误", "ERR_WRONG_PASSWORD");
ERR_RESET_PASSWORD_FAILED = add("设置密码失败", "ERR_RESET_PASSWORD_FAILED");
ERR_USER_LOCKED = add("用户已被锁定", "ERR_USER_LOCKED");
ERR_RETRIEVE_EMAIL_EXPIRED = add("找回密码邮件过期", "ERR_RETRIEVE_EMAIL_EXPIRED");
ERR_ILLEGAL_USER_NAME = add("无效的用户名", "ERR_ILLEGAL_USER_NAME");
ERR_USER_DELETE = add("用户已被删除", "ERR_USER_DELETE");
ERR_ACCOUNT_PWD_NULL = add("账号或密码不能为空", "ERR_ACCOUNT_PWD_NULL");
ERR_PHONE_ALREADY_BOUND = add("手机号已经被绑定", "ERR_PHONE_ALREADY_BOUND");
ERR_ILLEGAL_PHONE_ACCOUNT = add("非法手机号", "ERR_ILLEGAL_PHONE_ACCOUNT");
ERR_ILLEGAL_ACCOUNT_TYPE = add("非法账号类型", "ERR_ILLEGAL_ACCOUNT_TYPE");
ERR_ILLEGAL_EMAIL_ACCOUNT = add("非法邮箱", "ERR_ILLEGAL_EMAIL_ACCOUNT");
}
}