SpringMVC实现全局异常处理器

通过 @ControllerAdvice 注解,我们可以在一个地方对所有 @Controller 注解的控制器进行管理。
注解了 @ControllerAdvice 的类的方法可以使用 @ExceptionHandler@InitBinder@ModelAttribute 注解到方法上,这对所有注解了 @RequestMapping 的控制器内的方法都有效。

  1. @ExceptionHandler:用于捕获所有控制器里面的异常,并进行处理。
  2. @InitBinder:用来设置 WebDataBinderWebDataBinder 用来自动绑定前台请求参数到 Model 中。
  3. @ModelAttribute@ModelAttribute 本来的作用是绑定键值对到 Model 里,此处是让全局的@RequestMapping 都能获得在此处设置的键值对。

本文使用 @ControllerAdvice + @ExceptionHandler 进行全局的 Controller 层异常处理。只要设计得当,就再也不用在 Controller 层进行 try-catch 了!

一、经典案例

需求:希望通过全局统一的异常处理将自定义错误码以json的形式发送给前端。

1、统一返回结果类 ApiResult

首先,定义一个统一结果返回类,最终需要将这个结果类的内容返回给前端。

package com.tao.smp.exception;

/**
 * Api统一的返回结果类
 */
public class ApiResult {
    /**
     * 结果码
     */
    private String code;

    /**
     * 结果码描述
     */
    private String msg;


    public ApiResult() {

    }

    public ApiResult(ResultCode resultCode) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
    }

    /**
     * 生成一个ApiResult对象, 并返回
     *
     * @param resultCode
     * @return
     */
    public static ApiResult of(ResultCode resultCode) {
        return new ApiResult(resultCode);
    }

    public String getCode() {
        return code;
    }

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

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    @Override
    public String toString() {
        return "ApiResult{" +
                "code='" + code + '\'' +
                ", msg='" + msg + '\'' +
                '}';
    }
}

2、错误码枚举类 ResultCode

有了 ApiResult ,接下来需要定义一个枚举类, 来包含所有自定义的结果码。

package com.tao.smp.exception;

/**
 * 错误码
 */
public enum ResultCode {

    /**
     * 成功
     */
    SUCCESS("0", "success"),

    /**
     * 未知错误
     */
    UNKNOWN_ERROR("0x10001", "unkonwn error"),

    /**
     * 用户名错误或不存在
     */
    USERNAME_ERROR("0x10002", "username error or does not exist"),

    /**
     * 密码错误
     */
    PASSWORD_ERROR("0x10003", "password error"),

    /**
     * 用户名不能为空
     */
    USERNAME_EMPTY("0x10004", "username can not be empty");

    /**
     * 结果码
     */
    private String code;

    /**
     * 结果码描述
     */
    private String msg;


    ResultCode(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public String getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

3、自定义业务异常类 BusinessRuntimeException

接下来需要定义我们自己的业务异常类,以后和业务相关的异常通通抛出这个异常类,我们将错误码枚举变量的值存于其中。

package com.tao.smp.exception;

/**
 * 自定义业务异常
 */
public class BusinessRuntimeException extends RuntimeException {

    /**
     * 结果码
     */
    private String code;

    /**
     * 结果码描述
     */
    private String msg;

    /**
     * 结果码枚举
     */
    private ResultCode resultCode;


    public BusinessRuntimeException(ResultCode resultCode) {
        super(resultCode.getMsg());
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
        this.resultCode = resultCode;
    }

    public String getCode() {
        return code;
    }

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

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public ResultCode getResultCode() {
        return resultCode;
    }

    public void setResultCode(ResultCode resultCode) {
        this.resultCode = resultCode;
    }
}

4、全局异常处理类 GlobalExceptionResolver

最后便是定义全局异常处理类。
1. 通过 @ControllerAdvice 指定该类为 Controller 增强类。
2. 通过 @ExceptionHandler 自定捕获的异常类型。
3. 通过 @ResponseBody 返回 json 到前端。

注意一点:被@ControllerAdvice注解的全局异常处理类也是一个 Controller ,我们需要配置扫描路径,确保能够扫描到这个Controller。

package com.tao.smp.exception;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 全局Controller层异常处理类
 */
@ControllerAdvice
public class GlobalExceptionResolver {

    private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionResolver.class);

    /**
     * 处理所有不可知异常
     *
     * @param e 异常
     * @return json结果
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ApiResult handleException(Exception e) {
        // 打印异常堆栈信息
        LOG.error(e.getMessage(), e);
        return ApiResult.of(ResultCode.UNKNOWN_ERROR);
    }

    /**
     * 处理所有业务异常
     *
     * @param e 业务异常
     * @return json结果
     */
    @ExceptionHandler(BusinessRuntimeException.class)
    @ResponseBody
    public ApiResult handleOpdRuntimeException(BusinessRuntimeException e) {
        // 不打印异常堆栈信息
        LOG.error(e.getMsg());
        return ApiResult.of(e.getResultCode());
    }
}

二、测试

1、测试 TestController

package com.tao.smp.controller;

import com.tao.smp.exception.BusinessRuntimeException;
import com.tao.smp.exception.ResultCode;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * 测试异常的抛出
 */
@Controller
@RequestMapping("/")
public class TestController {

    /**
     * 测试返回异常信息
     * @return
     */
    @GetMapping("/exception")
    public String returnExceptionInfo() {

        if (1 != 2) {
            // 用户民错误或不存在异常
            throw new BusinessRuntimeException(ResultCode.USERNAME_ERROR);
        }

        return "success";
    }
}

这里写图片描述

这里写图片描述

猜你喜欢

转载自blog.csdn.net/hbtj_1216/article/details/81102063