抛弃丑陋的try-catch,优雅处理异常

news2025/1/22 8:38:14

随着业务逻辑变得越来越复杂,我们在编写代码时会遇到各种异常情况,这时就需要使用try-catch语句来捕获异常并进行处理。但是,大量的try-catch语句会让代码变得臃肿,不易维护,因此,我们需要一种优雅的方式来统一处理异常,减少代码中的try-catch语句。

比较下面两张图,看看您现在编写的代码属于哪一种风格?然后哪种编码风格您更喜欢?

丑陋的 try catch 代码块:

 

优雅的Controller:

 

那么问题来了,我们改如何实现第二种异常处理方式,如何优雅的处理各种异常?

异常处理的基本原则

在讲解如何减少try-catch语句之前,我们先来了解一下异常处理的基本原则。

首先,异常应该被及时捕获并进行处理。如果异常未被捕获,程序将崩溃并抛出未处理的异常,影响系统的稳定性。因此,我们需要在代码中合理地使用try-catch语句来捕获异常,并在catch块中进行处理。

异常应该被分类处理。不同类型的异常需要采取不同的处理方式,比如,对于业务逻辑异常,我们需要将异常信息返回给客户端,而对于系统异常,我们需要记录日志并通知管理员。

最后,异常处理应该是统一的。在一个应用程序中,我们可能会遇到很多不同的异常类型,如果每个异常都需要单独处理,会使代码变得很冗长。因此,我们需要将异常处理的逻辑抽象出来,实现统一的异常处理。

Spring Boot中的异常处理

Spring Boot提供了很多种方式来处理异常,比如使用@ControllerAdvice注解来定义一个全局的异常处理类,使用@ExceptionHandler注解来处理特定的异常类型等。下面,我们将介绍如何使用@ControllerAdvice注解来实现统一的异常处理。

定义异常处理类

我们可以使用@ControllerAdvice注解来定义一个全局的异常处理类,该类中的方法会被自动调用来处理异常。下面是一个示例:

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public ResponseData handleBusinessException(BusinessException e) {
        // 处理业务逻辑异常
        return new ResponseData(e.getCode(), e.getMessage(), null);
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseData handleException(Exception e) {
        // 处理系统异常
        return new ResponseData(500, "系统异常", null);
    }
}

在上面的代码中,我们定义了一个全局的异常处理类GlobalExceptionHandler,该类中定义了两个方法:handleBusinessException和handleException。其中,handleBusinessException方法用来处理业务逻辑异常,handleException方法用来处理系统异常。@ExceptionHandler注解指定了处理的异常类型,@ResponseBody注解将返回结果序列化成JSON格式,方便客户端进行处理。

自定义异常类

在上面的代码中,我们使用了BusinessException和Exception两个异常类来区分业务逻辑异常和系统异常。因此,我们需要定义这两个异常类。下面是一个示例:

public class BusinessException extends RuntimeException {

    private int code;

    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }
}

在上面的代码中,我们定义了一个BusinessException类,继承自RuntimeException。该类中包含了一个code字段,用来标识业务逻辑异常的类型。

抛出异常

在我们的业务逻辑中,如果遇到了异常情况,我们需要抛出对应的异常。下面是一个示例:

javaCopy codepublic User getUserById(Long id) {
    User user = userRepository.findById(id).orElse(null);
    if (user == null) {
        throw new BusinessException(1001, "用户不存在");
    }
    return user;
}

在上面的代码中,如果根据id未找到对应的用户,我们就会抛出BusinessException异常,该异常包含了错误码1001和错误信息"用户不存在"。

优雅的异常处理方式

通过上面的示例,我们已经实现了一个基本的统一异常处理,但是在实际开发中,我们还可以优化异常处理的方式,使代码更加优雅。

使用枚举类定义错误码

在上面的示例中,我们在BusinessException中定义了错误码,但是错误码的值可能会有重复,而且不太容易管理。因此,我们可以使用枚举类来定义错误码。下面是一个示例:

public enum ErrorCode {

    USER_NOT_FOUND(1001, "用户不存在"),
    PASSWORD_ERROR(1002, "密码错误"),
    ;

    private int code;
    private String message;

    ErrorCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

在上面的代码中,我们定义了一个ErrorCode枚举类,包含了多个错误码及其对应的错误信息。

使用自定义注解来简化代码

在上面的示例中,我们需要在每个抛出异常的方法中手动创建BusinessException对象,并指定错误码和错误信息。这样做的代码量比较大,而且不太优雅。因此,我们可以使用自定义注解来简化代码。下面是一个示例:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckUser {

    long id() default 0;

}

在上面的代码中,我们定义了一个CheckUser注解,用来标注需要检查用户的方法。该注解包含了一个id属性,用来指定用户的id。

下面是一个使用该注解的示例:

@GetMapping("/{id}")
@CheckUser(id = 1)
public User getUserById(@PathVariable("id") Long id) {
    User user = userService.getUserById(id);
    return user;
}

在上面的代码中,我们使用了@CheckUser注解,指定了id为1,表示需要检查id为1的用户是否存在。在CheckUserAspect切面中,我们会根据该注解的值来进行业务逻辑的处理。

使用AOP实现统一异常处理

在上面的示例中,我们使用了@ExceptionHandler注解来实现异常的处理。但是,如果我们有很多的Controller方法,每个方法都需要加上该注解,这样就会使代码量变得很大,而且不太优雅。因此,我们可以使用AOP来实现统一异常处理。下面是一个示例:

javaCopy code@Aspect
@Component
public class ExceptionAspect {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionAspect.class);

    @Pointcut("execution(public * com.example.springbootdemo.controller..*.*(..))")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object result;
        try {
            result = pjp.proceed();
        } catch (BusinessException e) {
            result = new Response<>(e.getCode(), e.getMessage());
        } catch (Exception e) {
            logger.error("系统异常", e);
            result = new Response<>(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
        }
        return result;
    }

}

在上面的代码中,我们定义了一个ExceptionAspect切面,用来处理所有Controller方法抛出的异常。@Pointcut注解用来定义切入点,表示所有public方法。@Around注解用来定义环绕通知,表示在目标方法执行前后都要执行该通知。在该通知中,我们可以处理抛出的异常,然后返回处理结果。

总结

通过上面的示例,我们实现了一个简单的统一异常处理。在实际开发中,我们可以根据需求进行一些优化,使代码更加简洁、优雅。异常处理是一个很重要的功能,需要我们在开发过程中认真对待,避免出现漏洞,保证系统的稳定性和安全性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/434315.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UDS统一诊断服务【六】访问时序参数0X83服务

文章目录 前言一、访问时序参数服务介绍二、数据格式2.1 请求报文2.2 子功能2.3 响应 三、举例 前言 本文介绍UDS统一诊断服务的访问时序参数0X83服务&#xff0c;希望能对你有所帮助 一、访问时序参数服务介绍 这个服务我目前在项目中没怎么用到过&#xff0c;先来看看ISO14…

【三十天精通Vue 3】第十三天 Vue 3 的插件详解

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: 三十天精通 Vue 3 文章目录 引言一、插件概述1.1 插件简介1.2 插件的使用1.3 插件的分类 二、…

五、vue基础-指令之v-bind

一、v-bind 前面要讲的一系列指令&#xff0c;主要是将值插入到模板内容中。 但是&#xff0c;除了内容需要动态来决定外&#xff0c;某些属性我们也希望动态来绑定。 比如动态绑定a元素的href属性&#xff1b;比如动态绑定img元素的src属性&#xff1b; 绑定属性我们使用v-…

智能电网中针对DOS和FDIA的弹性分布式EMA(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 ​智能电网是一种典型的信息物理融合系统,也是关系国民经济发展和国家安全的重大关键基础设施,其安全稳定运行至关重要。近年来…

java某百货店POS积分管理系统_积分点更新生成以及通票回收处理

百货店是生活中不可缺少的一部分&#xff0c;为了给顾客提供更方便的服务平台以及更好的服务质量&#xff0c;而设计了POS积分管理系统。百货店通过点积分的管理获得顾客更好的信誉&#xff0c;增加客户流量&#xff0c;获得更多的利益。在百货店经营的过程中&#xff0c;每天的…

软考 软件设计师上午题面向对象

面向过程和面向对象 省略面向对象可以省略过程&#xff0c;复杂事情简单化 类 类是对象的抽象&#xff0c;对象是类的实例 一般类是交通工具。特殊类是轮船飞机。因为他们是特殊的佳通工具&#xff0c;一个天上的一个海上的 对象 属性别名状态成员变量&#xff0c;方法也叫…

K_A31_002 基于STM32等单片机驱动315MHZ收发模块 串口显示

K_A31_002 基于STM32等单片机驱动315MHZ收发模块 串口显示 所有资源导航一、资源说明二、基本参数参数引脚说明 三、驱动说明原理&#xff1a;对应程序: 四、部分代码说明1、接线引脚定义1.1、STC89C52RC315MHZ收发模块1.2、STM32F103C8T6315MHZ收发模块 五、基础知识学习与相关…

通过Salesforce考试 (考证)后,如何在Trailhead上验证和维护证书?

随着Salesforce产品家族的不断壮大&#xff0c;学习者可以考的认证也在不断增多。从十几年前的几个认证&#xff0c;增长到现在的40多个认证。 在获得Salesforce认证之后&#xff0c;除了要将其放在LinkedIn和Trailblazer.me个人资料中&#xff0c;还有一种官方途径可以让其他…

靶机精讲之pwnOS1.0解法二

主机发现 基于前一解法 复现找到的漏洞文件利用文件 应该要想到如何利用ssh 构造利用语句 authorized_keys文件获取 访问免登录文件失败 敏感文件泄露库 发现敏感文件的经验&#xff08;精&#xff09; 按ctrlf搜索 .ssh 免密公钥 已经拿到公钥的数据 用公钥信息破解出私钥…

Redis入门介绍+linux安装

Redis是什么 Redis 是完全开源免费的&#xff0c;遵守BSD协议&#xff0c;是一个高性能(NOSQL)的key-value数据库,Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value&#xff08;键值对&#xff09;数据库&#xff0c;并提供多种语言的…

【HTML5】HTML5 多媒体标签 ① ( audio 音频标签 | 音频标签常见属性值设置 | 音频标签默认代码设置 | 音频标签设置多种类型音频文件 )

文章目录 一、HTML5 多媒体标签二、音频标签三、音频标签代码示例 ( 默认操作 )四、音频标签代码示例 ( 插入 mp3 / ogg 两种格式的音频 ) 一、HTML5 多媒体标签 传统 HTML 开发中 , 如果想要向网页中嵌入音频和视频 , 需要 使用 Flash 浏览器插件才能实现 ; 在 HTML5 中 , 使…

“智慧金融”精细化客户服务+营销获客

在新兴金融产品与智能服务创新科技手段的不断推动下&#xff0c;我国金融行业的发展已经实现由“金融信息化”向“智慧金融”的阶级跨越。金融行业在客户服务、业务流程、业务开拓等方面已得到全方位的“数智化”提升&#xff0c;实现了“AI金融”客户服务、风控、营销获客的智…

基于禅道二开领导报表

上周开会的时候公司项目总监说感觉最近开发人员很轻松&#xff0c;工作量不饱和。支付力度不够。 做为开发负责人&#xff0c;对项目总监这个说法我肯定需要给予响应&#xff0c;不然老板也在场&#xff0c;后续项目想要加资源啥的都无法解释。 关注我的人知道&#xff0c;之前…

客流统计分析系统增强售楼处、4S店飞单管理能力

客流统计分析系统可以为售楼处和4S店提供有效的飞单管理能力&#xff0c;使其能够更好地管理客户信息和提高销售效率。首先&#xff0c;客流统计分析系统可以对售楼处和4S店的客流进行实时监控和分析。通过使用摄像头、人脸识别等技术&#xff0c;可以对进店的客户进行统计和分…

whisper技术导读2

1、数据处理 根据最近利用互联网上的网络规模文本来训练机器学习系统的趋势&#xff0c;我们采用了一种极简的方法来进行数据预处理。与语音识别方面的许多工作相比&#xff0c;我们训练Whisper模型在没有任何显著标准化的情况下预测转录本的原始文本&#xff0c;依靠序列到序列…

ffmpeg 向流媒体服务器推RTSP 流时候的 交互过程以及接收到的 RTP包解析

之前写了RTSP服务端 和客户端拉流之间的交互流程 正好最近在看流媒体服务器 中RTSP的部分 copy了下源码 编译下发现不能正常播放 借此机会 记录下rtsp推流时候和服务器交互的流程 以上是推流端向服务器推流的时候 的整个流程 之后就是媒体数据的发送了 然后在看下vlc播…

electron_笔记

创建你的第一个应用: package.json: {"name": "my-electron-app","version": "1.0.0","description": "my demo","main": "main.js","scripts": {"dev": "electr…

Mac安装和卸载node和npm

1、官网下载 访问nodejs官网&#xff0c;点击稳定版&#xff0c;并下载 https://nodejs.org/en 2、安装 双击刚下载的文件&#xff0c;按步骤默认安装就行 3、 验证 安装完成后打开终端 npm -vnode -v如下图出现版本信息&#xff0c;说明安装成功 4、环境配置 打开M…

Vue中的嵌套路由

router官网-嵌套路由 实际生活中的应用界面&#xff0c;通常由多层嵌套的组件组合而成。同样地&#xff0c;URL 中各段动态路径也按某种结构对应嵌套的各层组件&#xff0c;例如&#xff1a; <body><div id"app"><h1>欢迎使用路由导航</h1&g…

差分信号输入隔离放大转换模块PCB焊接式0-20mV/0-±10mV/0-±20mV转0-5V/0-10V/4-20mA

概述&#xff1a; IPO压力应变桥信号处理系列隔离放大器是一种将差分输入信号隔离放大、转换成按比例输出的直流信号混合集成厚模电路。产品广泛应用在电力、远程监控、仪器仪表、医疗设备、工业自控等行业。该模块内部嵌入了一个高效微功率的电源&#xff0c;向输入端和输出端…