乐趣区

Spring-Boot-异常处理

前言

先谈谈“异常处理”这件事。下面有 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) 能处理异常吗?
答:可以的!所以对于局部比较公用的异常可以定义一个父类,抛出异常时可以抛出具体的子类异常,处理时,处理父类异常即可(即只用写一个方法处理一系列类似的异常)

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

● 拒绝黑盒应用 -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?__…

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

退出移动版