本文系全文转载,原文链接地址是:http://www.juvenxu.com/2011/03/30/exception-handling-best-practices/。只有最后一段是我的补充
作为一个已经写了近5年Java代码的程序员,我直到最近才算是基本明白了异常应该怎么用,这真是令人汗颜。事情是这样的,上周,和往常一样,我在开发一个很平常的应用,并且不得不面对各种各样的异常,比如常见的IOException,或者用到个第三方类库可能会给你返回ThirdPartyException,还有,我自己也会定义异常,姑且叫它MyOwnException。我是使用分层的架构写代码的,比如有个REST层,有个领域模型层,有持久化层,这本没什么问题,可当我发现一个接口要throws三四个或者更多的异常的时候,就觉得蛋疼了,这不仅看起来恶心,如调用者如何处理它们似乎也是个问题。这不是第一次,其实之前蛋疼过很多次了,我一直阅读各种OO设计相关书籍,以理解如何组织各种类和对象,可我还真没仔细考虑过如何组织异常。
好吧,为了以后不再蛋疼,我得弄清楚这个异常的用法。我希望能够明白如何组织异常才能使其变得整洁,而不是肆无忌惮地污染我精心设计的接口。
我翻阅了相关书籍,仔细查看了它们关于异常的描述,包括
Implementation Patterns — Kent Beck
Clean Code: A Handbook of Agile Software Craftsmanship — Robert C. Martin
Effective Java (2ed) — Joshua Bloch
Robust Java: Exception Handling, Testing, and Debugging — Stephen Stelting
边阅读边思考,最终基本理解了异常处理的最佳实践。值得一提的是,这几本书中 Joshua Bloch 的描述相比最为全面和深刻,Kent Beck 的最简单粗糙,Robert C. Martin 一书的那一章其实是 Michael Feathers 写的,也只能算是一般,Stephen Stelting 的描述看起来很全面,可太理论了,实例太少,没有说服力。
那异常处理的最佳实践到底是什么?首先要理解的是,异常到底是什么,我觉得异常分两类。
第一是业务异常,就是处理业务的时候80%的时候是没问题的,但可能有20%的时候事情没有按理想的方向发展。例如注册用户的时候,正常情况是注册成功,但可能用户提交请求的时候,系统发现用户名已经被别人注册了,这是就可以抛出一个UserAlreadyExistsException。
第二是系统异常,系统异常与具体业务流程没有直接的关系,例如编程错误导致的NullPointExcpetion,还有坏境问题,例如磁盘损坏或者网络连接不稳定造成了IOException。
先谈谈业务异常,业务异常应该是接口的一部分,该接口的用户应该能够处理该异常。也就是说,在实现接口之前,就应该定义清楚这个接口会抛出怎样的异常,例如注册用户这个接口就应该明确定义会抛出UserAlreadyExistsException,并辅以必要文档,那调用者就知道怎么去处理。如果你在接口声明异常,那你一定要确保接口的调用者能够处理之。例如对应于 UserAlreadyExistsException,用户界面可以提示用户该ID已存在,那用户可以使用另外的ID来注册。注意,处理异常不是简单的catch,然后随便打印点日志那么简单,业务异常的处理应当遵照业务流程。
而系统异常由于没有实际业务意义,调用者往往是不知道如何处理的。例如注册用户的时候收到一个IOException,你只会一头雾水。那系统异常应该如何处理呢?有这么几个选择:
能在底层处理的就本地处理掉,例如网络连接超时异常,可以编写代码尝试再次连接。
能翻译为业务异常的就翻译,有时候你可能发现某个IOException对应有实际的业务意义,但不要直接抛出,而是应该Exception Chaining技术来翻译,例如 throw new MyOwnException(“sorry, …”, ioe),这样既不至于丢失原始信息,业务接口也更容易理解。
实在无法处理的,就转换为RuntimeException,反正调用者也不知道怎么处理,那在接口中声明是没有意义的,只会造成调用者困惑并带来负担。这里同样应该使用Exception Chaining以避免信息丢失。我的实际经验告诉我,很多人完全都不考虑使用RuntimeException,这真是莫大的浪费。
有时候业务异常和系统异常的角色会相互转换,例如 FileNotFoundException 对于文件处理这个领域来说是业务异常,可对于我的应用来说,可能只是一个系统异常。
上述是我认为异常处理最核心最有用的实践,除此之外还有不少前人总结的经验,包括:
记录异常,但只记录一次。(不要丢失历史)
不要返回及传Null值。(避免NullPointException和不必要的检查)
尽量重用JDK自带的异常。(以降低学习成本)
在自定义异常中存储异常相关信息字段。(以方便日后处理)
永远不要直接忽略异常。(不要丢失历史)
为异常声明写Javadoc。(对你的用户有好些)
不要 throws 或者 catch 顶层的 Exception 类。(天知道Exception对应什么业务信息)
其实当我读毕前面提到的几本书,然后再Google相关文档的时候,就发现已经有人基本总结过这些内容了:Best Practices for Exception Handling, by Gunjan Doshi, 文章是2003年写的,但一点也不过时。相比之下我这篇文章有污染搜索引擎之嫌,不管怎样,这算是自我的一个总结,对英文不好的人应该也有些价值。
————————————————正文结束————————————————
原文写得很好,稍微长了点,我总结补充如下:
1、作者认为异常分为“业务异常”和“系统异常”,正好java也有CheckedException和UncheckedException之分,我感觉可以对应起来。
对于“业务异常”,上层类应该知道是如何处理的,所以将“业务异常”声明为CheckedException,作为接口的一部分,强制要求上层捕获处理,这是合理的
对于“系统异常”,上层类就算catch住了,也是无能为力,所以将其声明为CheckedException,意义是很小的,往往也只能继续向上抛出,或者记录日志就消化掉。反而会有不必要的try catch,影响代码的可读性。所以对于这类异常,可以声明为UncheckedException,不强制上层捕获处理
2、对于业务层无法处理的异常,不可避免地会抛到展现层。那么这个时候,至少要做两件事情,一个是记录日志,另一个是页面跳转,不让用户直接看到500。如果展现层用的是struts2框架的话,可以通过global-exception-handler来实现
3、如果用spring管理事务,那么业务层是不能把DAO层的异常消化掉的,否则的话spring管理的事务就无法生效了,在一个系统中考虑异常处理机制的时候,务必考虑这一点
4、对于MyOwnException,作者建议采用Exception Chaining来实现
分享到:
相关推荐
10. **最佳实践和案例研究** - 提供实际使用中的最佳实践,以及一些成功的案例,帮助读者更好地理解和应用这两个工具。 总的来说,这篇文章将为读者提供一个全面的视角,理解jBPM的历史发展,以及在面对新的流程...
7. **最佳实践**:教程可能会分享一些关于如何有效管理数据输出和确保数据安全性的最佳实践,这对于企业级RPA实施至关重要。 8. **集成其他RPA组件**:查看数据表活动通常是RPA流程的一部分,可能还会涉及到如何与...
7. **最佳实践**:分享在AA中使用JavaScript的最佳实践,以确保代码的可读性、可维护性和性能优化。 8. **社区互动**:鼓励观众参与RPA之家的讨论,共同学习和解决实际遇到的问题。 这个视频教程对于那些正在学习...
9. **最佳实践**:学习良好的编程习惯,如使用PreparedStatement防止SQL注入,释放数据库连接,避免内存泄漏等。 这个实战项目对于初学者来说是一个很好的练习,可以帮助他们更好地理解和掌握Web开发的基本流程,...
8. **最佳实践与案例研究**:可能包含真实世界中的RPA应用案例,帮助学习者更好地理解和应用所学知识。 通过这套教程,无论是初学者还是有一定经验的RPA从业者,都能提升自己的技能水平,为实现企业的数字化转型...
7. **最佳实践与案例分析**:Syed Pasha可能会分享一些最佳实践,以及他在实际项目中的经验,帮助学习者避免常见陷阱,提高自动化项目的成功率。 8. **社区支持与交流**:视频教程强调了在学习过程中遇到问题时,...
Java数据库连接(JDBC)是Java...以上就是关于Java数据库连接及连接池的一些基础知识,具体博客内容可能还包括更详细的示例代码和最佳实践,如事务管理、批处理等。对于Java开发者来说,熟练掌握这些内容是至关重要的。
8. **最佳实践和注意事项**:了解如何优化Twilio活动的使用,避免过度依赖,同时确保遵守通信法规。 通过观看此教程,初学者可以深入理解RPA与通信集成的可能性,而经验丰富的RPA开发者则能进一步提升其自动化解决...
9. **最佳实践**:教程可能还会提供一些使用通用记录器的最佳实践,比如避免过度依赖记录器、理解自动化流程的潜在风险等。 通过学习这个教程,初学者可以掌握Automation Anywhere的通用记录器功能,并逐步提升RPA...
10. **最佳实践**:了解在处理CSV文件时的一些最佳实践,例如,如何处理编码问题,如何避免数据丢失,以及如何优化性能。 通过本教程的学习,你不仅可以掌握UiPath中处理CSV文件的基本技巧,还能进一步提升你的RPA...
最佳实践可能包括参数化、异常处理、性能优化、日志记录等方面。 4. `DB2 SQL存储过程语法官方权威指南(翻译).mht`:这个文件提供了DB2 SQL存储过程的官方语法参考,可能包括创建、修改和执行存储过程的步骤,以及...
通过本教程的学习,观众不仅能掌握在Enterprise A2019中模拟按键的基本技巧,还能了解到RPA在实际工作中的应用策略和最佳实践,从而提升自身的RPA实施能力。在RPA之家社区,观众还可以与同行交流心得,共同探讨RPA...
7. **最佳实践**:教程可能还会分享编写高效、可维护Python脚本的技巧,以及如何将Python脚本与AA的工作流相结合的最佳实践。 通过这个教程,学习者不仅可以提升Python编程技能,还能掌握如何在RPA项目中有效地利用...
7. **最佳实践**:学习如何编写可维护、可扩展的RPA代码,遵循良好的编程习惯,以提高自动化流程的可靠性。 在学习过程中,配合视频教程,你可以在实践中不断巩固理论知识。同时,通过观看Edureka的专业讲解,你...
8. 遵循最佳实践:最后,学习者会了解到在实施RPA时的一些最佳实践,如何确保合规性,以及如何进行有效的变更管理和风险管理。 总的来说,通过观看这个视频教程,学习者将能够系统地了解RPA,掌握其基本原理和操作...
Java编程事项是一个涵盖广泛的主题,它包含了众多关于Java语言编程的最佳实践、常见陷阱以及高效编程技巧。这篇由多个来源整理转贴的资料,以.htm格式呈现,旨在帮助开发者提升编程技能,避免常见错误,理解Java的...
6. **最佳实践和案例研究**:分享实际项目中的经验和建议,展示RPA在不同业务场景中的应用。 通过这个教程,学习者不仅可以掌握RPA和UiPath的基础知识,还能了解到如何将这些技能应用于实际的Salesforce自动化项目...
它们详细介绍了配置选项、API 使用和最佳实践,帮助开发者快速上手。 在实际项目中,我们通常会结合 Spring Boot 和 Spring Security 进行配置,利用自动配置简化流程。通过阅读 `_SpringSecurity3_教程及官方参考...
综合来看,这个C++ Collection.web资源包旨在深入探讨C++的高级特性,包括异常处理、智能指针的比较、模板的使用、STL的最佳实践,以及C++对其他语言特性(如C#的属性)的实现。这些文章对于希望提高C++编程技能,...
chap 17:使用 Java Native Interface 的最佳实践... 106 1.性能缺陷... 107 2.正确性缺陷... 117 3.避免常见缺陷... 121 4.结束语... 128 Chap18:JNI设计实践之路... 129 一、 前言... 129 二、 JNI基础知识...