zoukankan      html  css  js  c++  java
  • Spring Boot 异常处理

    img

    前言

    先谈谈“异常处理”这件事。下面有 2 份伪代码,对比下:

    
    // ① 基于 if/else 判断
    if(deletePage(page) == E_OK){
      if(registry.deleteReference(page.name) == E_OK){
        if(configKeys.deleteKey(page.name.makeKey()) == E_OK){
          logger.log("page deleted");
        }else{
          logger.log("configKey not deleted");
        }
      }else{
        logger.log("deleteReference from registry failed");
      }
    }else{
      logger.log("delete failed");
      return E_RROR;
    }
    
    // ② 基于异常处理
    try{
      deletePage(page);
      registry.deleteReference(page.name);
      configKeys.deleteKey(page.name.makeKey());
    }catch(Exception e){
      logError(e);
    }
    

    可以看出,如果使用异常替代返回错误码,错误处理代码就能从主路径逻辑中分离出来,得到简化!

    ②中,基于异常处理的代码真的好吗?其实是丑陋不堪的,它搞乱了代码结构,把错误处理与正常流程混为一谈。最好把 try 和 catch 代码块的主体部分抽离出来,形成另外的函数。

    // ③ 优雅的异常处理逻辑
    public void delete(Page page){
      try{
        deletePageAndAllReferences(page);
      }catch(Exception e){
        logError(e);
      }
    }
    
    private void deletePageAndAllReferences(Page page) throw Exception{
      deletePage(page);
      registry.deleteReference(page.name);
      configKeys.deleteKey(page.name.makeKey());
    }
    
    private void logError(Exception e){
      logger.log(e.getMessage());
    }
    

    ③中,函数各司其职,更易于理解和修改了。

    总结:使用异常而不是错误码,优雅地使用异常!函数应该只做一件事,处理错误就是一件事。因此,处理错误的函数不该做其他事!

    在 Spring Boot 中处理异常

    1、默认的异常处理

    例如 401,404,500,5XX 等异常,Spring Boot 默认会跳转到预配置的页面,此处以 thymeleaf 模板引擎为例:

    + resources
      + templates
        + error
          - 401.html
          - 404.html
          - 500.html
    

    只需在 resources/templates/error/ 路径下添加对应的html文件即可。

    2、局部异常处理

    局部异常一般处理业务逻辑出现的异常情况,在 Controller 下使用 @ExceptionHandler 注解来处理异常。举个小例子:

    先定义 ResponseBean 和 ExceptionEnum 两个对象,辅助完成优雅的代码。

    /**
     * 统一响应
     * @author anoy
     */
    public class ResponseBean<T> {
    
        private int code;
    
        private String message;
    
        private T data;
    
        public ResponseBean(){}
    
        public ResponseBean(ExceptionEnum exceptionEnum){
            this.code = exceptionEnum.getCode();
            this.message = exceptionEnum.getMessage();
        }
    
        // 省略 setter/getter
    }
    
    
    /**
     * 异常类型枚举
     * @author anoy
     */
    public enum ExceptionEnum {
    
        GIRL_FRIEND_NOT_FOUND(100000, "girl friend not found");
    
        private int code;
    
        private String message;
    
        ExceptionEnum(int code, String message){
            this.code = code;
            this.message = message;
        }
    
        public int getCode() {
            return code;
        }
    
        public String getMessage() {
            return message;
        }
    }
    

    今天七夕,写个 GirlFriendNotFoundException(很有同感,是不是?)

    @Controller
    public class UserController {
    
        @RequestMapping("/friend/{id}")
        public String friend(@PathVariable("id") Long id) throws GirlFriendNotFoundException {
            if (id == 1L){
                throw new GirlFriendNotFoundException();
            }
            return "friend";
        }
    
        @ExceptionHandler(GirlFriendNotFoundException.class)
        @ResponseBody
        public ResponseBean handleGirlFriendNotFound(GirlFriendNotFoundException exception){
            loggerError(exception);
            return new ResponseBean(ExceptionEnum.GIRL_FRIEND_NOT_FOUND);
        }
        
        private void logError(Exception e){
            logger.error(e.getMessage());
        }
    }
    

    3、全局异常处理

    个人观点:全局异常应该处理系统故障级别的问题,像参数校验这种类型的异常,应该作为局部异常来处理,例如 Redis 连接断开,无法请求数据,这种异常就应该当做全局异常来处理,在异常处理的逻辑中,还应该添加通知到开发人员的功能,方便开发人员及时处理错误!

    全局异常处理,使用 @ControllerAdvice@ExceptionHandler 来配合。

    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        @ExceptionHandler(RedisConnectionFailureException.class)
        public void handlerRedisConnectionFailureException(RedisConnectionFailureException exception){
            logError(exception);
            noticeToDev();
        }
    
        private void logError(Exception e){
            logger.error(e.getMessage());
        }
    
        private void noticeToDev(){
            // 通知具体开发人员
        }
    
    }
    

    常见问题

    1、局部异常和全局异常处理同一种类型的 Exception,会发生什么结果?
    答:只会执行局部异常的处理逻辑!

    2、GirlFriendNotFoundException 继承了 RuntimeException, 使用
    @ExceptionHandler(RuntimeException.class) 能处理异常吗?
    答:可以的!所以对于局部比较公用的异常可以定义一个父类,抛出异常时可以抛出具体的子类异常,处理时,处理父类异常即可(即只用写一个方法处理一系列类似的异常)

    © 著作权归作者所有,转载或内容合作请联系作者

    img

    拒绝黑盒应用-Spring Boot 应用可视化监控

    并发Bug之源有三,请睁大眼睛看清它们

    史上最轻松入门之Spring Batch - 轻量级批处理框架实践

    Spring Cloud Gateway - 快速开始

    APM工具寻找了一圈,发现SkyWalking才是我的真爱

    Spring Boot 注入外部配置到应用内部的静态变量

    将 HTML 转化为 PDF新姿势

    Java 使用 UnixSocket 调用 Docker API

    Fastjson致命缺陷

    Service Mesh - gRPC 本地联调远程服务

    使用 Thymeleaf 动态渲染 HTML

    原文链接:https://mp.weixin.qq.com/s?__biz=MzU0MDEwMjgwNA==&mid=2247486183&idx=1&sn=68f7c40c250e403af95587f306a2c628&chksm=fb3f131ccc489a0a586fde9fe9963b37ccd4bb1b57ed77916a7f82f971ab4feb395a0c217929&token=1168253104&lang=zh_CN#rd

    本文由博客一文多发平台 OpenWrite 发布!

  • 相关阅读:
    @Autowired mapper 层次 bean 带红线
    java 类加载机制 阿里面试题
    liunx 修改ssh 端口22
    通过mysqlbinlog 恢复数据
    网页命名规则
    子选择符 、相邻选择符 、 兄弟选择符 、 伪类选择符
    css的一些基础知识
    HTML5的表单所有type类型
    一行代码解决各种IE兼容问题,IE6,IE7,IE8,IE9,IE10(复制)
    网站开发最常用的代码(复制)
  • 原文地址:https://www.cnblogs.com/springforall/p/11773982.html
Copyright © 2011-2022 走看看