实现全局异常处理

程序出现异常会将堆栈信息也打印出来,但是我们在面对一些业务场景的时候需要自定义一些异常返回,并且暴露给用户的只能是业务提示,例如账号不能重复等,而不能将sql执行异常或者运行时错误的详细信息返回出去。

一. 实现一个简单的全局异常处理

新建一个GlobalExceptionHandler类,使用@RestControllerAdvice注解标记在类名上即可成功实现一个异常处理类,随后在内容中定义@ExceptionHandler(Exception.class)来捕获实际产生的异常,例如:

@RestControllerAdvice
public class GlobalExceptionHandler{
    /**
     * 系统异常
     */
    @ExceptionHandler(Exception.class)
    public JSONObject handleException(Exception e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("msg", g.getMessage());
        return jsonObject;
    }
}

当然,实际环境下我们不会使用JSONObject来作为异常返回,同时我们进行全局异常处理一般都是对一些特定的异常,或者是自定义异常。以下我们参考ruoyi的统一返回类AjaxResult进行改造,这里说明以下AjaxResult继承HashMap是希望使用Map的键值对返回属性,后续我们可以自定义类属性来替代HashMap:

/**
 * 操作消息提醒
 * 
 * @author ruoyi
 */
public class AjaxResult extends HashMap<String, Object>{
    
    /**
     * 初始化一个新创建的 AjaxResult 对象
     * 
     * @param code 状态码
     * @param msg 返回内容
     * @param data 数据对象
     */
    public AjaxResult(int code, String msg, Object data)
    {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
        if (StringUtils.isNotNull(data))
        {
            super.put(DATA_TAG, data);
        }
    }
    
	/**
     * 返回错误消息
     * 
     * @param msg 返回内容
     * @return 警告消息
     */
    public static AjaxResult error(String msg)
    {
        return AjaxResult.error(msg, null);
    }
    
    /**
     * 返回错误消息
     * 
     * @param msg 返回内容
     * @param data 数据对象
     * @return 警告消息
     */
    public static AjaxResult error(String msg, Object data)
    {
        return new AjaxResult(500, msg, data);
    }

}
@RestControllerAdvice
public class GlobalExceptionHandler{
    /**
     * 系统异常
     */
    @ExceptionHandler(Exception.class)
    public AjaxResult handleException(Exception e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        return AjaxResult.error(e.getMessage());
    }
    
    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return AjaxResult.error(message);
    }
}

至此,我们已经实现一个简单的全局异常处理,如上所示,总结以下几点:

  • 使用@RestControllerAdvice标记类名
  • 使用@ExceptionHandler(XxxException.class)标记方法名,表示拦截处理XxxException异常
  • 方法体内具体描述拦截处理

二. 进阶改造自定义异常

通常,我们除了描述jdk默认的异常之外,还会自定义契合业务主体本身的业务异常。

自定义运行时异常比较简单,我们只需要继承RuntimeException即可,例如:

public class MyException extends RuntimeException{

    public MyException(){
    }
}

我们自定义了一个MyException异常,结合第一部分的内容,我们需要捕获该异常,即:

/**
* 自定义异常
*/
@ExceptionHandler(DemoModeException.class)
public AjaxResult handleMyException(DemoModeException e){
    return AjaxResult.error("自定义异常被捕获");
}

实际场景中,我们为了方便管理异常情况,通常会定义一个异常结果枚举类,然后自定义异常只需要传入对应枚举类参数即可。

接下来提供一套完整的实战环境下的异常处理以供参考,首先定义一套参考的枚举类(参考自GitHub仓库-miaosha:https://github.com/qiurunze123/miaosha)

定义异常枚举类

public enum ResultStatus {
    SUCCESS(0, "成功"),
    FAILD(-1, "失败"),
    EXCEPTION(-1, "系统异常"),
    PARAM_ERROR(10000, "参数错误"),
    SYSTEM_ERROR(10001, "系统错误"),
    FILE_NOT_EXIST(10002, "文件不存在"),
    FILE_NOT_DOWNLOAD(10003, "文件没有下载"),
    FILE_NOT_GENERATE(10004, "文件没有生成"),
    FILE_NOT_STORAGE(10005, "文件没有入库"),
    SYSTEM_DB_ERROR(10006, "数据库系统错误"),
    FILE_ALREADY_DOWNLOAD(10007, "文件已经下载"),
    DATA_ALREADY_PEXISTS(10008, "数据已经存在"),


    /**
     * 注册登录
     */
    RESIGETR_SUCCESS(20000, "注册成功!"),
    RESIGETER_FAIL(200001, "注册失败!"),
    CODE_FAIL(200002, "验证码不一致!"),

    /**
     * check
     */
    BIND_ERROR(30001, "参数校验异常:%s"),
    ACCESS_LIMIT_REACHED(30002, "请求非法!"),
    REQUEST_ILLEGAL(30004, "访问太频繁!"),
    SESSION_ERROR(30005, "Session不存在或者已经失效!"),
    PASSWORD_EMPTY(30006, "登录密码不能为空!"),
    MOBILE_EMPTY(30007, "手机号不能为空!"),
    MOBILE_ERROR(30008, "手机号格式错误!"),
    MOBILE_NOT_EXIST(30009, "手机号不存在!"),
    PASSWORD_ERROR(30010, "密码错误!"),
    USER_NOT_EXIST(30011, "用户不存在!"),

    /**
     * 订单模块
     */
    ORDER_NOT_EXIST(60001, "订单不存在"),

    /**
     * 秒杀模块
     */
    MIAO_SHA_OVER(40001, "商品已经秒杀完毕"),
    REPEATE_MIAOSHA(40002, "不能重复秒杀"),
    MIAOSHA_FAIL(40003, "秒杀失败");

    /**
     * 商品模块
     */
    private int code;
    private String message;

    private ResultStatus(int code, String message) {
        this.code = code;
        this.message = message;
    }

    private ResultStatus(Object... args) {
        this.message = String.format(this.message, args);
    }

    public int getCode() {
        return this.code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return this.message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getName() {
        return this.name();
    }

    public String getOutputName() {
        return this.name();
    }

    @Override
    public String toString() {
        return this.getName();
    }
}

自定义全局异常类用来接收ResultStatus枚举类

public class GlobleException extends RuntimeException {


    private ResultStatus status;

    public GlobleException(ResultStatus status) {
        super();
        this.status = status;
    }

    public ResultStatus getStatus() {
        return status;
    }

    public void setStatus(ResultStatus status) {
        this.status = status;
    }
}

改造统一返回类AjaxResult

/**
 * 改造AjaxResult
 * 
 */
public class AjaxResult {
    
    private ResultStatus status;
    private int code;
    private String message;
    
    public AjaxResult(ResultStatus status) {
        this.code = status.getCode();
        this.message = status.getMessage();
        this.status = status;
    }
    
    /**
     * 返回错误消息
     * 
     * @param 枚举信息 status
     * @return 警告消息
     */
    public static AjaxResult error(ResultStatus status)
    {
        return new AjaxResult(status);
    }

}

定义全局异常处理类

@RestControllerAdvice
public class GlobalExceptionHandler{
    
    /**
     * 自定义验证异常
     */
    @ExceptionHandler(GlobleException.class)
    public AjaxResult handleGlobleException(GlobleException e)
    {
        log.error(e.getStatus().getMessage(), e);
        return AjaxResult.error(e.getStatus());
    }
    
}

使用

// ...
if (user == null) {
    /**
    * 对应枚举类MOBILE_NOT_EXIST(30009, "手机号不存在!")
    */
    throw new GlobleException(MOBILE_NOT_EXIST);
}

这里触发的逻辑即可被我们的自定义的全局异常所捕获,捕获流程如下:

image-20221024112134548

参考资料:

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果不想使用@ControllerAdvice注解,我们也可以通过实现HandlerExceptionResolver接口来实现全局异常处理。具体步骤如下: 1. 创建一个实现HandlerExceptionResolver接口。 2. 在实现resolveException方法,该方法会在全局异常发生时被执行。 3. 在resolveException方法中,我们可以根据不同的异常型进行不同的处理,比如返回自定义错误信息或者跳转到指定页面。 4. 最后,我们需要将该注册到Spring框架中,可以通过在配置文件中进行配置或者使用注解的方式进行注册。 以下是一个简单的示例: ```java public class GlobalExceptionHandler implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // 返回自定义错误信息 ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("errorMsg", "系统错误,请稍后重试!"); modelAndView.setViewName("error"); return modelAndView; } } ``` 在上述代码中,我们实现了HandlerExceptionResolver接口并重写了resolveException方法。在该方法中,我们将错误信息存入ModelAndView中,并将viewName设置为"error",表示跳转到error页面显示错误信息。 最后,我们需要将该注册到Spring框架中。可以通过在配置文件中进行配置或者使用注解的方式进行注册。例如,在Spring配置文件中添加如下配置: ```xml <bean id="globalExceptionHandler" class="com.example.GlobalExceptionHandler"/> ``` 在上述配置中,我们将GlobalExceptionHandler注册为Spring的一个bean,并使用该处理全局异常。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值