一个成熟的Java项目如何优雅地处理异常

Java攻城师 2021-01-21 13:43:36
java 项目 一个 优雅 成熟


(一)概述

异常处理是一个系统最重要的环节,当一个项目变得很大的时候,异常处理和日志系统能让你快速定位到问题。对于用户或者接口调用者而言,优雅的异常处理可以让调用者快速知道问题所在。本文将介绍如何优雅地处理异常。

(二)使用通用的返回体

我们希望所有的错误都以Json的方式返回给客户,因此拿出上次写的通用返回体,新建一个类CommonResult记录返回体。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult {
private int code;
private String message;
private Object data;
}
复制代码

新建一个枚举类ResponseCode集成code和message。

public enum ResponseCode {
// 系统模块
SUCCESS(0, "操作成功"),
ERROR(1, "操作失败"),
SERVER_ERROR(500, "服务器异常"),
// 通用模块 1xxxx
ILLEGAL_ARGUMENT(10000, "参数不合法"),
REPETITIVE_OPERATION(10001, "请勿重复操作"),
ACCESS_LIMIT(10002, "请求太频繁, 请稍后再试"),
MAIL_SEND_SUCCESS(10003, "邮件发送成功"),
// 用户模块 2xxxx
NEED_LOGIN(20001, "登录失效"),
USERNAME_OR_PASSWORD_EMPTY(20002, "用户名或密码不能为空"),
USERNAME_OR_PASSWORD_WRONG(20003, "用户名或密码错误"),
USER_NOT_EXISTS(20004, "用户不存在"),
WRONG_PASSWORD(20005, "密码错误"),
;
ResponseCode(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
private Integer code;
private String msg;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
复制代码

(三)自定义运行时异常

自定义一个运行时异常类,构造方法传入异常参数即可。

public class MyException extends RuntimeException{
private String msg;
public MyException(String msg) {
super(msg);
}
}
复制代码

(四)编写一个统一的异常处理类

异常处理类是整个异常处理核心,SpringBoot中提供了ControllerAdvice注解来拦截异常,使用RestControllerAdvice注解保证了返回Json格式。

如果拦截到的异常属于MyException,则按Json格式返回错误结果。

@RestControllerAdvice
public class ExceptionController {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = Exception.class)
public CommonResult exceptionHandler(Exception e){
//如果抛出的异常属于自定义异常,就以JSON格式返回
if (e instanceof MyException){
return new CommonResult(ResponseCode.ERROR.getCode(),ResponseCode.ERROR.getMsg(),"自定义的错误为:"+e.getMessage());
}
//如果都不是就打印出异常的信息
return new CommonResult(ResponseCode.ERROR.getCode(),ResponseCode.ERROR.getMsg(),"错误的信息为:"+e.getMessage());
}
}
复制代码

(五)测试

为了看初效果,这里手动抛出一个异常来测试,新建IndexController,手动抛出异常

@RestController
public class IndexController {
@RequestMapping(value = "/index",method = RequestMethod.GET)
public String index(){
throw new MyException("测试");
}
}
复制代码

查看调用结果:

在这里插入图片描述

(六)对实体类的校验

有这样一个场景,登陆注册时用户名和密码有长度限制,手机号有格式限制,如果不满足要求就无法注册。这个功能前端可以限制,但是对于后端接口而言,也需要进行限制,万一前端没有限制住呢。

导入两个校验依赖包:

<!--校验-->
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>
复制代码

编写实体类,在每个属性上加上校验包的验证参数。

@Data
public class Register {
@Length(max = 20,min = 4,message = "用户名长度需要在4到20个字符之间")
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1[3|4|5|8][0-9]d{8}$",message = "电话号码格式不正确")
private String phone;
@Length(max = 20,min = 4,message = "密码长度需要在4到20个字符之间")
@NotBlank(message = "密码不能为空")
private String password;
}
复制代码

我们在需要使用的方法中增加@Valid注解进行校验,比如这个post请求中我要校验。

@PostMapping("/register")
public CommonResult register(@Valid @RequestBody Register register){
//一连串注册的业务
userService.registerUser(register);
return new CommonResult(ResponseCode.SUCCESS.getCode(),ResponseCode.SUCCESS.getMsg(),"");
}
复制代码

@Valid在校验失败的情况下会报出参数不合法的异常,还是在统一的异常处理类中捕获异常,如果是MethodArgumentNotValidException,就取出对应的message数据。

@RestControllerAdvice
public class ExceptionController {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = Exception.class)
public CommonResult exceptionHandler(Exception e){
//如果属于参数校验异常,就抛出校验的错误
if (e instanceof MethodArgumentNotValidException){
MethodArgumentNotValidException methodArgumentNotValidException= (MethodArgumentNotValidException) e;
return new CommonResult(ResponseCode.ERROR.getCode(),ResponseCode.ERROR.getMsg(),
"校验错误:"+methodArgumentNotValidException.getBindingResult().getFieldError().getDefaultMessage());
}//如果是自定义的异常,就给出具体的异常原因
else if (e instanceof MyException){
return new CommonResult(ResponseCode.ERROR.getCode(),ResponseCode.ERROR.getMsg(),"自定义的错误为:"+e.getMessage());
}
//如果都不是就打印出异常的信息
return new CommonResult(ResponseCode.ERROR.getCode(),ResponseCode.ERROR.getMsg(),"错误的信息为:"+e.getMessage());
}
}
复制代码

(七)测试校验

接下来就可以测试校验的功能了,通过postman访问

在这里插入图片描述

如果输入参数不满足之前的设置,就会给出具体的错误信息。而不是抛出让人无法接收的报错:

在这里插入图片描述

(八)总结

许多人写代码时最不考虑的就是异常处理,简单地实现需求就好了,所以才会导致许多不可预估的bug出现。

参考:《2020最新Java基础精讲视频教程和学习路线!》

链接:https://juejin.cn/post/691981...

版权声明
本文为[Java攻城师]所创,转载请带上原文链接,感谢
https://segmentfault.com/a/1190000039048424

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云