异常场景分析
优质博文:IT-BLOG-CN
为了防止黑客从前台异常信息,对系统进行攻击。同时,为了提高用户体验,我们都会都抛出的异常进行拦截处理。
一、异常处理类
Java
把异常当做是破坏正常流程的一个事件,当事件发生后,就会触发处理机制。Java
有一套独立的异常处理机制,在遇到异常时,方法并不返回任何值(返回值属于正常流程),而是抛出一个封装了错误信息的对象。
Throwable
所有的异常对象都派生于Throwable
类的一个实例。在一个Throwable
里面可以获取如下信息:
■ 获取堆栈跟踪信息。源代码中哪个类,哪个方法,第几行出现了问题……从当前代码到最底层的代码调用链都可以查出来。追踪获取底层的异常信息。
■ 获取没抛出来的其他Throwable
。一次只能抛出一个异常,如果发生了多个异常,其他异常就不会被抛出,这时可以通过加入suppressed
异常列表来解决(JDK7 以后才有)
Throwable
类只有两个直接继承者:Error
和Exception
。然后Exception
又分为RuntimeException
和CheckedException
。
Error
在Java
中, 由系统环境问题引起的异常,一般都继承于 Error 类。对于Error
类:
■ 一般开发者不要自定义Error
子类,因为它代表系统级别的错误。与一般的程序无关。
■ 在Java
异常处理机制中,Error
不强制捕获或声明,也就是不强制处理。因为程序本身对此类错误无能为力。一般情况下我们只要把堆栈跟踪信息记录下来就行。
Exception
在Java
中,除了系统环境问题引起的异常,一般都继承于Exception
类。Exception
分为RuntimeException
和CheckedException
。CheckedException
必须要捕获或声明。而RuntimeException
不强制。
CheckedException
: 在Java
中,直接或间接因为“资源”问题引起的异常,一般属于检查异常CheckedException
。检查异常继承于Exception
,而不继承于RuntimeException
。对于检查异常:
■ 必须捕获或声明
■ 交给关心这个异常的方法处理
■ 异常处理器应该引导用户接下来怎么办,至少做到安全退出
RuntimeException
: 在Java
中,由于接口方法使用不当造成的异常,一般属于RuntimeException
,也就是运行时异常。对于RuntimeException
:
■ 如果你调用服务方法的方式不正确,你应该马上修改代码,避免发生RuntimeException
■ 如果是用户方法调用你的方法的方式不正确,你应该立刻抛出RuntimeException
,强制让使用者修正代码或改变使用方式,防止问题蔓延
■ 一般情况下,不要捕获或声明RuntimeException
。因为问题在于你的程序本身有问题,如果你用异常流程处理了,反而让正常流程问题一直存在
Uncheck Exception
Error
和RuntimeException
统称为非检查异常。两者的共同点就是都不被强制捕获或声明。实际上两者描述问题的范围完全没有交集。
二、Java 异常处理机制
【1】抛出异常: 当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。
【2】捕获异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着 Java 程序的终止。
对于运行时异常、错误或可查异常,Java 技术所要求的异常处理方式有所不同。
■ 由于运行时异常的不可查性,为了更合理、更容易地实现应用程序,Java 规定,运行时异常将由 Java 运行时系统自动抛出,允许应用程序忽略运行时异常。
■ 对于方法运行中可能出现的 Error,当运行方法不欲捕捉时,Java 允许该方法不做任何抛出声明。因为,大多数 Error 异常属于永远不能被允许发生的状况,也属于合理的应用程序不该捕捉的异常。
■ 对于所有的可查异常,Java 规定:一个方法必须捕捉,或者声明抛出方法之外。也就是说,当一个方法选择不捕捉可查异常时,它必须声明将抛出异常。
■ 能够捕捉异常的方法,需要提供相符类型的异常处理器。所捕捉的异常,可能是由于自身语句所引发并抛出的异常,也可能是由某个调用的方法或者 Java 运行时 系统等抛出的异常。也就是说,一个方法所能捕捉的异常,一定是 Java 代码在某处所抛出的异常。简单地说,异常总是先被抛出,后被捕捉的。
■ 任何 Java 代码都可以抛出异常,如:自己编写的代码、来自 Java 开发环境包中代码,或者 Java 运行时系统。无论是谁,都可以通过 Java 的 throw 语句抛出异常。
■ 从方法中抛出的任何异常都必须使用 throws 子句。捕捉异常通过 try-catch 语句或者 try-catch-finally 语句实现。
总体来说,Java 规定:对于可查异常必须捕捉、或者声明抛出。允许忽略不可查的 RuntimeException 和 Error。
捕获异常
【1】try-catch 语句:在 Java 中,异常通过 try-catch 语句结束。关键词 try 后的一对大括号将一块可能发生异常的代码包起来,称为监控区域。Java 方法在运行过程中出现异常,则创建异常对象。将异常抛出监控区域之 外,由 Java 运行时系统试图寻找匹配的 catch 子句以捕获异常。若有匹配的 catch 子句,则运行其异常处理代码,try-catch 语句结束。
匹配的原则是:如果抛出的异常对象属于 catch 子句的异常类,或者属于该异常类的子类,则认为生成的异常对象与 catch 块捕获的异常类型相匹配。
需要注意的是,一旦某个 catch 捕获到匹配的异常类型,将进入异常处理代码。一经处理结束,就意味着整个 try-catch 语句结束。其他的 catch 子句不再有匹配和捕获异常类型的机会。对于有多个 catch 子句的异常程序而言,应该尽量将捕获底层异常类的 catch 子句放在前面,同时尽量将捕获相对高层的异常类的 catch 子句放在后面。否则,捕获底层异常类的 catch 子句将可能会被屏蔽。
【2】try-catch-finally 语句:try-catch 语句还可以包括第三部分,就是 finally 子句。它表示无论是否出现异常,都应当执行的内容。
无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。在以下 4 种特殊情况下,finally 块不会被执行:
■ 在 finally 语句块中发生了异常。
■ 在前面的代码中用了 exit()退出程序。
■ 程序所在的线程死亡。
■ 关闭 CPU。
三、全局异常处理
编写一个异常拦截类,如下:@ControllerAdvice
,顾名思义,这是一个增强的Controller
。使用这个Controller
,可以实现三个方面的功能:①、全局异常处理;②、全局数据绑定;③、全局数据预处理;灵活使用这三个功能,可以帮助我们简化很多工作,需要注意的是,这是SpringMVC
提供的功能,在SpringBoot
中可以直接使用,下面分别来看。
import com.edu.tools.R;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;/*** @description:* @author: zzx* @createDate: 2020/6/2* @version: 1.0*/
@ControllerAdvice
public class GlobalExceptionHandler {//很重要,括号类制定需要拦截的异常,也可以进行定制化@ExceptionHandler(Exception.class)@ResponseBodypublic R error(Exception e){e.printStackTrace();//R表示我们给前端返回的接口格式return R.error().message("执行全局异常处理。。。");}
}
测试:
四、自定义异常处理
【1】创建自定义异常类继承RuntimeException
类。
/*** @description: 自定义异常类,包含了有参合无参构造器* @author: zzx* @createDate: 2020/6/2* @version: 1.0*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BusinessException extends RuntimeException {private Integer code;//状态码private String msg;//异常消息
}
【2】将自定义的异常类添加到拦截的Handler
中
/*** @description:* @author: zzx* @createDate: 2020/6/2* @version: 1.0*/
@ControllerAdvice
public class GlobalExceptionHandler {//拦截自定义异常@ExceptionHandler(BusinessException.class)@ResponseBodypublic R error(BusinessException e){e.printStackTrace();return R.error().code(e.getCode()).message(e.getMsg());}
}
【3】在业务代码根据需求进行手动抛出即可,业务代码展示:throw new BusinessException
(20001,“手动异常抛出”);
/*** <p>* 讲师 前端控制器* </p>** @author zhengzhaoxiang* @since 2020-06-01*/
@RestController
@RequestMapping("/eduservice/edu-teacher")
public class EduTeacherController {@Autowiredprivate EduTeacherService eduTeacherService;/*** @Description 获取所有数据* @Author zhengzhaoxiang* @Date 2020/6/2 15:27* @Param []* @Return void*/@GetMapping("findAll")public R findAll(){List<EduTeacher> list = eduTeacherService.list(null);try{int i = 1/0;} catch (Exception e){//手动抛出异常throw new BusinessException(20001,"手动异常抛出");}return R.ok().data("items",list);}
}
自定义异常处理类测试:
五、案例
某日11点23分 xxx处理中量(5s)智能检测发现异常下降60%
事后分析得知其根因是: 修改模板配置时,多配了个占位符%S
导致字符串格式化的方法出错,程序未进行异常捕获,输入页加载查询接口失败,前端页面进行降级,关闭了入口,影响用户操作。
研发团队给出的解决方案:针对读取配置文案异常需要进行捕获降级,不能影响业务主流程。
案例分析
针对这类错误,以往的解决方案通常是打补丁式的修正,即:针对已发生异常的配置项进行容错处理(对解析函数添加try…catch
,如上方)。
然而,每一次异常几乎都是发生在不同的配置上,也就是说我们的补丁对于生产事件的产生几乎没有任何抑制作用。补丁式修正只能治标而不能治本,我们需要一种能治本的方案。下面就让我来给大家介绍这么一种方案(如果您有更好的方案,望不吝赐教)
最佳实践
程序初始化时将所有配置读取并解析后保存为本地变量,应用每次直接读取本地Config
变量。监听QConfig
,当配置发生变化时调用步骤1的方法重新解析配置文件。如果解析时发生异常,记录错误日志/报警,并使用旧的配置。示例代码:
public class ConfigStatic {private static final Logger LOGGER = LoggerFactory.getLogger(ConfigStatic.class);private static ConfigStatic instance = new ConfigStatic();private static final String TITLE = "ConfigStatic";private final boolean initializeSuccess;static {// 将ConfigStatic.refresh注册至ConfigurationFunc,当配置发生变化的时候ConfigStatic.refresh将被调用ConfigurationFunc.registerListener(TITLE, ConfigStatic::refresh);}private ConfigStatic() {initializeSuccess = initialize();}public static ConfigStatic getInstance() {return instance;}private static void refresh() {// 创建新配置(构造函数会调用initialize进行初始化)ConfigStatic newConfig = new ConfigStatic();if (newConfig.initializeSuccess) {// 如果新实例初始化成功,将其替换为instanceinstance = newConfig;}}private Set<Integer> hsFcAddjustPriceAgencyId;// 此处省略其他配置...private boolean initialize() {final String title = "ConfigStatic.initialize";try {this.directCompareNormalAirlines = ConfigurationFunc.getHashSet("yPlusXProductConfig", ",");// 此处省略其他配置...return true;} catch (Exception ex) {LogManager.build(title, ex).error();return false;}}
}
相关文章:

异常场景分析
优质博文:IT-BLOG-CN 为了防止黑客从前台异常信息,对系统进行攻击。同时,为了提高用户体验,我们都会都抛出的异常进行拦截处理。 一、异常处理类 Java把异常当做是破坏正常流程的一个事件,当事件发生后,…...

Leetcode: 0001-0010题速览
Leetcode: 0001-0010题速览 本文材料来自于LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解 遵从开源协议为知识共享 版权归属-相同方式…...
计算机的错误计算(一百一十二)
摘要 计算机的错误计算(六十三)与(六十八)以及(六十九)分别探讨了大数与 附近数以及 附近数 的余切函数的计算精度问题。本节讨论余切序列(即迭代 )的计算精度问题。 余切序列是指…...

C++基础(7)——STL简介及string类
目录 1.STL简介 1.1什么是 1.2STL的历史版本 1.3STL的六大组件 编辑 1.4有用的网址 2.string类 2.1string的多种定义方式 2.2string的插入 2.2.1尾插(push_back) 2.2.2insert插入 2.3拼接(append) 2.4删除 2.4.1尾…...
配置Nginx以支持通过HTTPS回源到CDN
要配置Nginx以支持通过HTTPS回源到CDN,你需要确保Nginx已正确配置SSL,并且能够处理来自CDN的HTTPS请求。以下是一个简化的Nginx配置示例,它配置了SSL并设置了代理服务器参数以回源到CDN: server {listen 443 ssl;server_name you…...

yolov10+strongsort的目标跟踪实现
此次yolov10deepsort不论是准确率还是稳定性,再次超越了之前的yolodeepsort系列。 yolov10介绍——实时端到端物体检测 YOLOv10 是清华大学研究人员在 UltralyticsPython 清华大学的研究人员在 YOLOv10软件包的基础上,引入了一种新的实时目标检测…...

C# 字符与字符串
本课要点: 1、字符类Char的使用 2、字符串类String的使用 3、可变字符串****StringBuilder 4、常见错误 一 何时用到字符与字符串 问题: 输出C#**课考试最高分:**98.5 输出最高分学生姓名:张三 输出最高分学生性别&#x…...
在Ubuntu 16.04上使用LEMP安装WordPress的方法
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 简介 WordPress 是互联网上最流行的 CMS(内容管理系统)。它允许您在 MySQL 后端和 PHP 处理的基础上轻松设置灵…...
显示器放大后,大漠识图识色坐标偏移解决方法
原因分析: 显示器分辨率较高,DPI设置放大125% or 150% or 200%,游戏打开时也会默认会根据显示器的放大比例自行放大,但是大漠综合管理工具抓图不会放大; 解决方法: 1、大漠综合管理…...

C++容器之list基本使用
目录 前言 一、list的介绍? 二、使用 1.list的构造 2.list iterator的使用 3.list capacity 🥇 empty 🥇size 4.list element access 🥇 front 🥇 back 5.list modifiers 🥇 push_front 🥇 po…...

Redis-哨兵
概念 Redis Sentinel 相关名词解释 注意: 哨兵机制不负责存储数据,只是对其它的redis-server进程起到监控的作用哨兵节点,也会搞一个集合,防止一个挂了 ⼈⼯恢复主节点故障 用户监控: 实际开发中,对于服务器后端开发,监控程序,是很重要的 服务器长期运行,总会有一些意外,…...

Pikachu-Sql-Inject - 基于时间的盲注
基于时间的盲注: 就是前端的基于time 的盲注,什么错误信息都看不到,但是还可以通过特定的输入,判断后台的执行时间,从而确定注入。 mysql 里函数sleep() 是延时的意思,sleep(10)就是数据库延时10 秒返回内…...

JAVA开源项目 旅游管理系统 计算机毕业设计
本文项目编号 T 063 ,文末自助获取源码 \color{red}{T063,文末自助获取源码} T063,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析5.4 用例设计 六、核…...

景联文科技入选《2024中国AI大模型产业图谱2.0版》数据集代表厂商
近日,大数据产业领域头部媒体数据猿携手上海大数据联盟联合发布了备受瞩目的《2024中国AI大模型产业图谱2.0版》。以大数据与AI为代表的智能技术为主要视角,聚焦全产业链,为业内提供更为专业直观的行业指导。 景联文科技凭借高质量数据集&…...

【C语言】内存函数的使用和模拟实现
文章目录 一、memcpy的使用和模拟实现二、memmove的使用和模拟实现三、memset的使用四、memcmp的使用 一、memcpy的使用和模拟实现 在之前我们学习了使用和模拟实现strncpy函数,它是一个字符串函数,用来按照给定的字节个数来拷贝字符串,那么问…...

在WPF中实现多语言切换的四种方式
在WPF中有多种方式可以实现多语言,这里提供几种常用的方式。 一、使用XML实现多语言切换 使用XML实现多语言的思路就是使用XML作为绑定的数据源。主要用到XmlDataProvider类. 使用XmlDataProvider.Source属性指定XML文件的路径或通过XmlDataProvider.Document指定…...
30min 的OpenCV learning Note
1.安装python和pycharm与环境搭配 打开Windows终端:(winR)(一般使用清华镜像网站安装库比较快) pip install opencv-contrib-python -i https://pypi.mirrors.ustc.edu.cn/simple 或者 python -m pip install open…...

C--编译和链接见解
欢迎各位看官!如果您觉得这篇文章对您有帮助的话 欢迎您分享给更多人哦 感谢大家的点赞收藏评论 感谢各位看官的支持!!! 一:翻译环境和运行环境 在ANSIIC的任何一种实现中,存在两个不同的环境1,…...
【QT Quick】基础语法:基础类与控件
QML 的基础类和控件中,我们可以看到主要的几个分类:基础控件类、窗口类以及组件类。以下是对这些控件及其属性、继承关系等的详细讲解: 控件关系总结 QtObject 是所有 QML 对象的基类。它定义了基础属性,主要用于逻辑和数据封装…...

使用 SSH 连接 Docker 服务器:IntelliJ IDEA 高效配置与操作指南
使用 SSH 连接 Docker 服务器:IntelliJ IDEA 高效配置与操作指南 本文详细介绍了如何在 2375 端口未开放的情况下,通过 SSH 连接 Docker 服务器并在 Idea 中进行开发。通过修改用户权限、生成密钥对以及配置 SSH 访问,用户可以安全地远程操作…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...