当前位置: 首页 > 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文件的正确加载。 具体修改步骤…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型

在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重&#xff0c;适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解&#xff0c;并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...

Matlab实现任意伪彩色图像可视化显示

Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中&#xff0c;如何展示好看的实验结果图像非常重要&#xff01;&#xff01;&#xff01; 1、灰度原始图像 灰度图像每个像素点只有一个数值&#xff0c;代表该点的​​亮度&#xff08;或…...