千家信息网

spring Boot项目怎么处理全局异常

发表于:2025-02-01 作者:千家信息网编辑
千家信息网最后更新 2025年02月01日,这篇文章主要介绍"spring Boot项目怎么处理全局异常",在日常操作中,相信很多人在spring Boot项目怎么处理全局异常问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对
千家信息网最后更新 2025年02月01日spring Boot项目怎么处理全局异常

这篇文章主要介绍"spring Boot项目怎么处理全局异常",在日常操作中,相信很多人在spring Boot项目怎么处理全局异常问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"spring Boot项目怎么处理全局异常"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

前言

异常的处理在我们的日常开发中是一个绕不过去的坎,在Spring Boot 项目中如何优雅的去处理异常,正是我们这一节课需要研究的方向。

异常的分类

在一个Spring Boot项目中,我们可以把异常分为两种,第一种是请求到达Controller层之前,第二种是到达Controller层之后项目代码中发生的错误。而第一种又可以分为两种错误类型:1. 路径错误 2. 类似于请求方式错误,参数类型不对等类似错误。

定义ReturnVO和ReturnCode

为了保持返回值的统一,我们这里定义了统一返回的类ReturnVO,以及一个记录错误返回码和错误信息的枚举类ReturnCode,而具体的错误信息和错误代码保存到了response.properties中,使用流进行读取。

ReturnVO
public class ReturnVO {    private static Properties properties = ReadPropertiesUtil.getProperties(System.getProperty("user.dir") + CommonUrl.RESPONSE_PROP_URL);    /**     * 返回代码     */    private String code;    /**     * 返回信息     */    private String message;    /**     * 返回数据     */    private Object data;    public Object getData() {        return data;    }    public void setData(Object data) {        this.data = data;    }    public String getMessage() {        return message;    }    public void setMessage(String message) {        this.message = message;    }    public String getCode() {        return code;    }    public void setCode(String code) {        this.code = code;    }    /**     * 默认构造,返回操作正确的返回代码和信息     */    public ReturnVO() {        this.setCode(properties.getProperty(ReturnCode.SUCCESS.val()));        this.setMessage(properties.getProperty(ReturnCode.SUCCESS.msg()));    }    /**     * 返回代码,这里需要在枚举中去定义     * @param code     */    public ReturnVO(ReturnCode code) {        this.setCode(properties.getProperty(code.val()));        this.setMessage(properties.getProperty(code.msg()));    }    /**     * 返回数据,默认返回正确的code和message     * @param data     */    public ReturnVO(Object data) {        this.setCode(properties.getProperty(ReturnCode.SUCCESS.val()));        this.setMessage(properties.getProperty(ReturnCode.SUCCESS.msg()));        this.setData(data);    }    /**     * 返回错误的代码,以及自定义的错误信息     * @param code     * @param message     */    public ReturnVO(ReturnCode code, String message) {        this.setCode(properties.getProperty(code.val()));        this.setMessage(message);    }    /**     * 返回自定义的code,message,以及data     * @param code     * @param message     * @param data     */    public ReturnVO(ReturnCode code, String message, Object data) {        this.setCode(code.val());        this.setMessage(message);        this.setData(data);    }    @Override    public String toString() {        return "ReturnVO{" +                "code='" + code + '\'' +                ", message='" + message + '\'' +                ", data=" + data +                '}';    }}
ReturnCode

其他的错误处理只需要在枚举类中添加对应的异常即可,枚举的名称要定义为异常的名称,这样可以直接不用对其他的代码进行修改,添加一个新的异常时,仅仅添加枚举类中的字段和properties文件中的属性。

public enum ReturnCode {    /** 操作成功 */    SUCCESS("SUCCESS_CODE", "SUCCESS_MSG"),    /** 操作失败 */    FAIL("FAIL_CODE", "FAIL_MSG"),    /** 空指针异常 */    NullPointerException("NPE_CODE", "NPE_MSG"),    /** 自定义异常之返回值为空 */    NullResponseException("NRE_CODE", "NRE_MSG"),    /** 运行时异常 */    RuntimeException("RTE_CODE","RTE_MSG"),    /** 请求方式错误异常 */    HttpRequestMethodNotSupportedException("REQUEST_METHOD_UNSUPPORTED_CODE","REQUEST_METHOD_UNSUPPORTED_MSG"),    /** INTERNAL_ERROR */    BindException("BIND_EXCEPTION_CODE","BIND_EXCEPTION_MSG"),    /** 页面路径不对 */    UrlError("UE_CODE","UE_MSG");    private ReturnCode(String value, String msg){        this.val = value;        this.msg = msg;    }    public String val() {        return val;    }    public String msg() {        return msg;    }    private String val;    private String msg;}
response.properties

这里我自定义了一些异常用于后面的测试,在我们实际的项目中需要定义很多的异常去完善。

SUCCESS_CODE=2000SUCCESS_MSG=操作成功FAIL_CODE=5000FAIL_MSG=操作失败NPE_CODE=5001NPE_MSG=空指针异常NRE_CODE=5002NRE_MSG=返回值为空RTE_CODE=5001RTE_MSG=运行时异常UE_CODE=404UE_MSG=页面路径有误REQUEST_METHOD_UNSUPPORTED_CODE=4000REQUEST_METHOD_UNSUPPORTED_MSG=请求方式异常BIND_EXCEPTION_CODE=4001BIND_EXCEPTION_MSG=请求参数绑定失败

路径错误处理

这里的路径错误处理方式是采用了实现ErrorController接口,然后实现了getErrorPath()方法:

/** * 请求路径有误 * @author yangwei * @since 2019-01-02 18:13 */@RestControllerpublic class RequestExceptionHandler implements ErrorController {    @Override    public String getErrorPath() {        return "/error";    }    @RequestMapping("/error")    public ReturnVO errorPage(){        return new ReturnVO(ReturnCode.UrlError);    }}

这里可以进行测试一下:

使用ControllerAdvice对其他类型的异常进行处理

类似于到达Controller之前的请求参数错误,请求方式错误,数据格式不对等等错误都归类为一种,这里仅仅展示请求方式错误的处理方式。

/** * 全局异常处理类 * @author yangwei * * 用于全局返回json,如需返回ModelAndView请使用ControllerAdvice * 继承了ResponseEntityExceptionHandler,对于一些类似于请求方式异常的异常进行捕获 */@RestControllerAdvicepublic class GlobalExceptionHandler extends ResponseEntityExceptionHandler {    private static Properties properties = ReadPropertiesUtil.getProperties(System.getProperty("user.dir") + CommonUrl.RESPONSE_PROP_URL);    /**     * 重写handleExceptionInternal,自定义处理过程     **/    @Override    protected ResponseEntity handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {        //这里将异常直接传给handlerException()方法进行处理,返回值为OK保证友好的返回,而不是出现500错误码。        return new ResponseEntity<>(handlerException(ex), HttpStatus.OK);    }    /**     * 异常捕获     * @param e 捕获的异常     * @return 封装的返回对象     **/    @ExceptionHandler(Exception.class)    public ReturnVO handlerException(Throwable e) {        ReturnVO returnVO = new ReturnVO();        String errorName = e.getClass().getName();        errorName = errorName.substring(errorName.lastIndexOf(".") + 1);        //如果没有定义异常,而是直接抛出一个运行时异常,需要进入以下分支        if (e.getClass() == RuntimeException.class) {            returnVO.setMessage(properties.getProperty(valueOf("RuntimeException").msg()) +": "+ e.getMessage());            returnVO.setCode(properties.getProperty(valueOf("RuntimeException").val()));        } else {            returnVO.setMessage(properties.getProperty(valueOf(errorName).msg()));            returnVO.setCode(properties.getProperty(valueOf(errorName).val()));        }        return returnVO;    }}

这里我们可以进行测试:

@RestController@RequestMapping(value = "/user")public class UserController {    @Autowired    private IUserService userService;    @PostMapping(value = "/findAll")    public Object findAll() {        throw new RuntimeException("ddd");    }    @RequestMapping(value = "/findAll1")    public ReturnVO findAll1(UserDO userDO) {        System.out.println(userDO);        return new ReturnVO(userService.findAll1());    }   @RequestMapping(value = "/test")    public ReturnVO test() {        throw new RuntimeException("测试非自定义运行时异常");    }}

直接在浏览器访问findAll,默认为get方法,这里按照我们期望会抛出请求方式异常的错误:

访问findAll1?id=123ss,这里由于我们接受的UserDOid属性是Integer类型,所以这里报一个参数绑定异常:

结合AOP使用,放入公用模块减少代码的重复

我们上节课使用AOP对于全局异常处理进行了一次简单的操作,这节课进行了完善,并将其放入到我们的公用模块,使用时只需导入jar包,然后在启动类配置扫描包路径即可

/** * 统一封装返回值和异常处理 * * @author vi * @since 2018/12/20 6:09 AM */@Slf4j@Aspect@Order(5)@Componentpublic class ResponseAop {    @Autowired    private GlobalExceptionHandler exceptionHandler;    /**     * 切点     */    @Pointcut("execution(public * indi.viyoung.viboot.*.controller..*(..))")    public void httpResponse() {    }    /**     * 环切     */    @Around("httpResponse()")    public ReturnVO handlerController(ProceedingJoinPoint proceedingJoinPoint) {        ReturnVO returnVO = new ReturnVO();        try {            Object proceed = proceedingJoinPoint.proceed();            if (proceed instanceof ReturnVO) {                returnVO = (ReturnVO) proceed;            } else {                returnVO.setData(proceed);            }        }  catch (Throwable throwable) {            // 这里直接调用刚刚我们在handler中编写的方法            returnVO =  exceptionHandler.handlerException(throwable);        }        return returnVO;    }}

做完这些准备工作,以后我们在进行异常处理的时候只需要进行以下几步操作:

  • 引入公用模块jar包

  • 在启动类上配置扫描包路径

  • 如果新增异常的话,在枚举类中新增后,再去properties中进行返回代码和返回信息的编辑即可(注意:枚举类的变量名一定要和异常名保持一致

到此,关于"spring Boot项目怎么处理全局异常"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0