当前位置: 首页 > news >正文

正确的 Java 异常处理

        我们来谈谈痛点吧。由于我的职责,我必须使用许多不同的服务(进行编辑、进行代码审查......);不同的团队通常会编写所有这些服务,每当涉及到处理错误并从服务转发错误时,有时我的眼睛就会开始流泪。让我尝试告诉您哪些代码在我看来是不可接受的错误处理以及我认为应该如何处理。

        一般来说,最初问题隐藏在服务分析的缺陷中。通常,对于如何抛出错误没有参考要求。一般来说,发生这种情况有两个原因:第一个是急于开发新服务,第二个是分析师信任开发人员的经验。在这种情况下,分析师只需告诉开发人员:“好吧,稍后给我一个错误消息的示例,我将在汇合中附加它。”

        让我们继续看例子;让我们看看这种方法在开发中的后果。

首先不要做的就是抛出 RuntimeException:

public Object badExample() {throw new RuntimeException("Something wrong!");
}

展示:

 

调用服务或客户端将收到 500 错误,并且无法理解其请求出了什么问题。您认为在这种情况下需要多长时间才能发现问题?它会随着你的代码量成比例增长。如果你有一个方法调用链,那么在服务内部调用方法时处理此类消息就会变得更加困难。

如何改进呢?首先,让我们创建一个错误处理程序;几乎所有框架都开箱即用;在我的示例中,我将使用 Spring 框架处理程序。

例子:

@ControllerAdvice
public class ApiExceptionHandler {@ExceptionHandler(value = {RuntimeException.class})public ResponseEntity<Object> handler(RuntimeException e) {HttpStatus badRequest = HttpStatus.INTERNAL_SERVER_ERROR;return new ResponseEntity<>(e.getMessage(), badRequest);}...

展示:

 

现在我们看到了消息,但是消息的格式是字符串,而不是 JSON。我们很快就会解决这个问题。

        理想情况下,项目中的所有服务都应具有相同的错误消息格式。至少有两个字段是消息本身和内部整数错误代码。我想没有必要说明为什么消息文本不足以及需要多少资源来处理该字符串。

例子:

public class ExampleApiError {private String message;private Integer code;private LocalDateTime dateTime;...

        我们将在错误处理程序中填充此类。但正如我所说,抛出 RuntimeException 是一种不好的做法,因此您需要创建自己的类来抛出错误,在该类的构造函数中我们将传递一条消息和一个错误代码。

public class ApiException extends RuntimeException {private final int code;public ApiException(String msg, int code) {super(msg);this.code = code;}...

似乎一切都更加清楚了,但即使在这里,问题也开始了;每个人都以不同的方式创建传递给类构造函数的参数。有些直接在方法中创建消息和错误代码。

例子:

public Object badExample() {throw new ApiException("Something wrong!", 10);
}

在代码中找到这样的地方仍然很困难。嗯,首先想到的是创建常量类,一个类用于消息,第二个类用于消息代码。

例子:

public static final String SOMETHING_WRONG = "Something wrong";
public static final int SOMETHING_WRONG_CODE = 10;public Object badExample() {throw new ApiException(SOMETHING_WRONG, SOMETHING_WRONG_CODE);
}

        当您知道错误代码在哪里时,这已经更好、更具可读性并且更容易找到。

        但是,如果您没有微服务,那么将所有内容存储在一个类中可能不是一个好主意。消息开始变得越来越大,因此最好根据方法的功能来分离常量类,这些常量类将与 ProductExceptionConstant、PaymentExceptionConstant 等一起使用。

        但这还不是全部。对某些人来说,这可能看起来很过时,但常量需要创建为枚举或接口。接口中的变量默认是静态的、公共的和最终的。我并不反对这种做法;最重要的是一切都应该是统一的;如果您开始通过接口进行操作,则继续这样做;无需混合。在其中一个项目中,我看到同一团队使用三种不同的常量方法。你不必这样做。)

        我将再展示一个在审核过程中引起我注意的真实项目的示例。

        首先,开发人员决定他返回的所有实体都将包含错误消息和错误代码,并且状态将始终为 200,在我看来,这会误导调用者。嗯,一个常量的例子。

public enum ErrorsEnum {DEFAULT_ERROR(ErrorsConstants.DEFAULT_ERROR, "400"),REFRESH_TOKEN_NOT_VALID(ErrorsConstants.REFRESH_TOKEN_NOT_VALID, "400.1"),USER_NOT_FOUND(ErrorsConstants.USER_NOT_FOUND, "400.2"),

在我看来,代码 9 3 \ 4 丢失了。默认情况下,在这种情况下,您可以使用数字 Pi。

        如果您对我的方法感兴趣,那就继续吧。最方便的事情是创建一个接口,它将成为每个人都有约束力的契约。

public interface ExceptionBase {String getMsg();Integer getCode();
}

并且需要更改异常类的构造函数。

public ApiException(ExceptionBase e) {super(e.getMsg());this.code = e.getCode();
}

现在,每个尝试抛出异常的人都会明白,我们必须将实现接口的对象传递给构造函数。最合适的选择是 Enum。

例子:

/*** This class is intended for declaring exceptions that occur during order processing. code range* 101-199.*/
public enum OrderException implements ExceptionBase {ORDER_NOT_FOUND("Order not found.", 101),ORDER_CANNOT_BE_UPDATED("Order cannot be updated,", 102);OrderException(String msg, Integer code) {this.msg = msg;this.code = code;}private final String msg;private final Integer code;@Overridepublic String getMsg() {return msg;}@Overridepublic Integer getCode() {return code;}
}

使用示例:

public Object getProduct(String id) {if (id.equals("-1")) {throw new ApiException(OrderException.ORDER_NOT_FOUND);}
...

服务器响应:

 因此,我们有以下异常包模型。

 

正如您所看到的,没什么复杂的;逻辑很容易阅读。在此示例中,我在 ApiException 类中留下了一个接受字符串的构造函数,但为了可靠性,最好将其删除。

通常,代码中的大多数不一致都是由于缺乏代码检查或检查不力造成的。最常见的借口是,“这是一个临时解决方案;我们稍后会修复它”,但不,它不会那样工作;没有人会费心寻找哪里有临时​​解决方案,哪里有永久解决方案。事实证明,“暂时的就是永久的”。

如果您有很多相互通信的服务,那么通过制作一种错误消息格式,您将大大简化编写客户端库的工作。例如,使用Retrofit时,一旦编写了处理程序核心,您只需更改接口中的方法和接收的对象即可。

结论

错误处理是代码中非常重要的部分;它可以轻松地找到代码中的问题区域,并且还允许外部客户端了解他们在使用端点时做错了什么,因此您应该从编写项目的第一阶段就对此给予适当的关注。

示例代码在这里。

我希望读完这篇文章后你的生活会变得更轻松一些。一切成功。

相关文章:

正确的 Java 异常处理

我们来谈谈痛点吧。由于我的职责&#xff0c;我必须使用许多不同的服务&#xff08;进行编辑、进行代码审查......&#xff09;&#xff1b;不同的团队通常会编写所有这些服务&#xff0c;每当涉及到处理错误并从服务转发错误时&#xff0c;有时我的眼睛就会开始流泪。让我尝试…...

RTT(RT-Thread)时钟管理

目录 时钟管理 时钟节拍 RTT工程目录结构介绍 配置文件&#xff1a;rtconfig.h 获取系统节拍 获取系统节拍数函数 实例 定时器 RT_Thread定时器介绍 定时器源码分析&#xff08;了解即可&#xff09; rt_system_timer_init (硬件定时器初始化) rt_system_timer_thr…...

基础实验篇 | uORB消息读写与自定义实验(二)

导读 uORB是PX4/Pixhawk系统中非常重要且关键的模块之一&#xff0c;是用于无人机模块间通信的协议机制。本篇将详细介绍uORB并详细拆解uORB消息读写与自定义实验全流程&#xff08;二&#xff09;。 基础实验篇 | uORB消息读写与自定义实验(二) 01 RflySim平台的uORB消息读写…...

k8s pod数据存储Volumes

一、说在前面的话 在 Kubernetes 的 Deployment 中&#xff0c;您可以使用多种类型的 Volumes 来管理 Pod 中的数据。 作用是用来共享目录及配置&#xff0c;不用在每个pod里进行配置。 本文主要概述怎么使用HostPath、PersistentVolumeClaim、ConfigMap。 二、k8s有哪些Vol…...

ZYNQ在Petalinux系统下双网口同网段的实现

ZYNQ在Petalinux系统下双网口同网段的实现 1.开发环境 采用了赛灵思zynq xc7z100芯片&#xff0c;外部挂载了两个网口phy芯片&#xff08;marvell 88e1510&#xff09;&#xff0c;且两个网口phy芯片公用MDIO管脚&#xff0c;bd配置如下&#xff1a; 2.问题说明 忙去了&am…...

突破传统监测模式:业务状态监控HM的新思路 | 京东云技术团队

一、传统监控系统的盲区&#xff0c;如何打造业务状态监控。 在系统架构设计中非常重要的一环是要做数据监控和数据最终一致性&#xff0c;关于一致性的补偿&#xff0c;已经由算法部的大佬总结过就不再赘述。这里主要讲如何去补偿&#xff1f;补偿的方案哪些&#xff1f;这就…...

7-16 验证“哥德巴赫猜想” (20 分)

7-16 验证“哥德巴赫猜想” &#xff08;20 分) 数学领域著名的“哥德巴赫猜想”的大致意思是&#xff1a;任何一个大于2的偶数总能表示为两个素数之和。比如&#xff1a;24519&#xff0c;其中5和19都是素数。本实验的任务是设计一个程序&#xff0c;验证20亿以内的偶数都可以…...

GEE学习02 --设置Jupyter Notebook的打开路径

直接双击Jupyter Notebook 桌面图标运行时&#xff0c;打开的文件路径是默认的&#xff1a;C&#xff1a;\用户\用户名 如果使用python命令提示符打开jupyter notebook &#xff0c; 而我新建的GEE学习文件夹在另一个路径&#xff0c;可以直接修改默认的保存路径&#xff1a;…...

stm32与上位机电脑间最快的通信方式是什么?

对于小型多关节机械臂的控制电路设计&#xff0c;选择合适的通信方式可以提高MCU与上位机之间的实时性。以下是一些在STM32上常用的通信方式&#xff0c;你可以根据你的具体需求选择适合的&#xff1a; 串口通信&#xff08;UART&#xff09;&#xff1a;串口通信是一种常见的…...

pytorch学习——卷积神经网络——以LeNet为例

目录 一.什么是卷积&#xff1f; 二.卷积神经网络的组成 三.卷积网络基本元素介绍 3.1卷积 3.2填充和步幅 3.2.1填充&#xff08;Padding&#xff09; 填充是指在输入数据周围添加额外的边界值&#xff08;通常是零&#xff09;&#xff0c;以扩展输入的尺寸。填充可以在卷…...

stm32 mpu6050 cubemx DMP法读取角度

文章目录 前言一、相关文件二、cubemx配置三、代码变量初始化主循环 总结 前言 文件 记录使用dmp库来读取mpu6050的角度。 这是参考文件 参考1–主要参考 github参考 参考2 参考三 一、相关文件 相关文件在这里下载&#xff08;未填&#xff0c;不过可以在上面的git中下载&a…...

.Net6 Core Web API 配置 log4net + MySQL

目录 一、导入NuGet 包 二、添加配置文件 log4net.config 三、创建MySQL表格 四、Program全局配置 五、帮助类编写 六、效果展示 小编没有使用依赖注入的方式。 一、导入NuGet 包 ---- log4net 基础包 ---- Microsoft.Extensions.Logging.Log4Net…...

校园跑腿小程序运营攻略

作为一名校园跑腿小程序的运营者&#xff0c;你可能会面临诸如用户获取、平台推广、服务质量保证等挑战。在本篇推文中&#xff0c;我将为你提供一些关键的运营策略&#xff0c;帮助你成功运营校园跑腿小程序。 1. 用户获取和留存 用户是校园跑腿小程序成功的关键。以下是一些…...

InfluxDB2如何求增量数据

需求 项目中需要接入电表设备&#xff0c;求用电量。 按天和设备统计用电量 按天统计用电量 统计总用电量 存在的问题 difference 函数可以求增量&#xff0c;但是以上计算均存在一个问题&#xff0c;比如xx设备有8.1号和8.2号的数据&#xff0c;我统计每天的用电量&#xf…...

Flink作业调度的9种状态

1.什么是作业调度 Flink 通过 Task Slots 来定义执行资源。每个 TaskManager 有一到多个 task slot&#xff0c;每个 task slot 可以运行一条由多个并行 task 组成的流水线。 这样一条流水线由多个连续的 task 组成&#xff0c;比如并行度为 n 的 MapFunction 和 并行度为 n 的…...

8、Kubernetes核心技术 - ConfigMap

目录 一、ConfigMap概述 二、ConfigMap创建 2.1、命令行方式创建 2.2、yaml 文件方式创建 三、ConfigMap查询 四、ConfigMap更新 4.1、kubectl edit方式 4.2、kubectl apply方式 五、ConfigMap使用 5.1、spec.env 【环境变量】 5.2、spec.envFrom 【环境变量】 5.3…...

音视频--DTMF信号发送及检测

参考资料 https://zh.wikipedia.org/wiki/%E5%8F%8C%E9%9F%B3%E5%A4%9A%E9%A2%91https://www.cnblogs.com/lijingcheng/p/4454932.html 1. DTMF是什么 1.1 DTMF定义 双音多频信号&#xff08;英语&#xff1a;Dual-Tone Multi-Frequency&#xff0c;简称&#xff1a;DTMF&a…...

阿里云容器服务助力极氪荣获 FinOps 先锋实践者

作者&#xff1a;海迩 可信云评估是中国信息通信研究院下属的云计算服务和软件的专业评估体系&#xff0c;自 2013 年起历经十年发展&#xff0c;可信云服务评估体系已日臻成熟&#xff0c;成为政府支撑、行业规范、用户选型的重要参考。 2022 年 5 月国务院国资委制定印发《…...

C++ 通过time.windows.com获取时间

C++ 通过time.windows.com获取时间 在C++中,你可以使用 <ctime>头文件中的 time()函数来获取当前的系统时间。然后,你可以使用 <ctime>头文件中的 localtime()函数将时间转换为本地时间,并从中获取小时、分钟和秒。 以下是一个示例代码,演示如何通过time.windo…...

MPLAB加载c文件为什么不能添加到工程中的source files中

MPLAB加载c文件为什么不能添加到工程中的source files中 因为你安装好MAPLAB软件之后你的编译器是默认的编译器&#xff0c;所以当你添加C文件时&#xff0c;软件是不认识C文件的&#xff0c;只有手动的将编译器改成自安装的PICC编译器才能进行C文件的正确加载。 具体修改步骤…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...