SpringBoot - 全局异常处理

实现全局异常处理的目的,是为了给用户一个友好的错误提示,并且不需要在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");
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Demon-HY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值