
Java 统一异常处理 (配置文件集中化定义)
0、前言
无论任何项目,都避免不了在运行期间出现的一些异常,并伴随着因业务逻辑的需要而给出相应的提示,使得系统变得更加友好,这类提示处理,我们统称为异常处理(exceptiona handling)。
在项目中异常处理所抛出的异常码、异常提示 ,都需要进行一定的封装,以确保异常的统一,提高程序的可维护性。而不是随心所欲的来进行异常提醒,如:一些硬编码异常信息(throw new Exception("系统处理异常")),随着想项目的变大、开发人员的不同,这些异常码可能会五花八门,没有统一标准,给用户提示、给开发很容易带来些许的困惑。
本文不是讲解如何正确使用try、catch、finally等进行异常捕获,而是就异常码、异常信息进行封装,通过配置文件进行集中化定义,来统一异常处理,让异常处理变得更标准化、统一化,方便维护、管理。
1、异常处理
异常处理,又称为错误处理,提供了处理程序运行时出现的任何意外或异常情况的方法。异常处理使用 try、catch 和 finally 关键字来尝试可能未成功的操作,处理失败,以及在事后清理资源。
异常发生的原因有很多,通常包含以下几大类:
- 用户输入了非法数据。
- 要打开的文件不存在。
- 网络通信时连接中断,或者JVM内存溢出。
这些异常有的是因为用户错误引起,有的是程序错误引起的。
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
所有的异常类是从 java.lang.Exception 类继承的子类。
Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。
Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
Error 用来指示运行时环境发生的错误。例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
异常类有两个主要的子类:IOException 类和 RuntimeException 类。
本文就针对处理的是Exception类异常。
2、统一异常处理
本实战中将异常码、异常信息进行封装,通过properties配置文件进行集中化定义,并支持国际化异常码的定义,来统一异常处理。
2.1 消息结果的封装
全系统统一返回的数据格式为:
标准的json字符串,statusCode:状态码,msg:提示信息,data:结果数据(以实际数据而定的json)。
定义一个实体类Result,用来封装消息返回数据,如下:
2.2 异常码、异常信息配置文件定义化
将异常码、异常信息统一集中定义到properties配置文件中,避免硬编码在代码中,方便维护,便于后期变动统一修改。异常码文件位于项目resources目录下\resources\error\,如:
异常码文件名统一格式:模块名_error_zh_CN.properties/
异常码统一格式定义,具体以实际项目情况而定,可参考如下标准定义:
封装异常码工具类ErrorUtils,用于从异常码文件中获取错误提示信息等,如下:
2.3 异常类封装
本文封装两类异常:
- 系统级异常:指系统级别的,如:网络通信时连接中断、系统连接、超时等异常
- 业务处理异常:指用户输入了非法数据等业务逻辑存在的异常
(其他类别异常,可自行封装,如SQL类异常)
(1)异常基类BaseException,所有异常类都继承此类,
(2)系统级异常SystemException,如下:
(3)业务处理异常类BusinessException,如下:
(4)异常工具类ExecptionUtils
为方便在业务代码中进行统一异常调用,特封装异常工具类ExecptionUtils,如下:
2.4 全局异常捕获
一般异常捕获都是通过try/catch、throw new等方式进行捕获,而频繁的这样操作,有时让人觉得麻烦,代码变得不是那么的干净,尤其业务复杂的场合。就像下面这种:
这样的代码既不简洁好看 ,我们敲着也烦, 一般我们可能想到用拦截器去处理。而在spring中提供了更好的方案,注解@ControllerAdvice和@ExceptionHandler,进行全局统一异常处理。
本文定义全局异常捕获类GlobalExceptionHandler,如下:
2.5 应用
将上述定义封装的异常,进行实际应用。
下述只是为了进行异常应用测试,并不符合实际业务场景。
以用户登录接口的service层UserServiceImpl类实现讲解,代码如下:
此例有三类场合异常处理:
(1)不带参的逻辑异常处理
throw ExecptionUtils.businessException("EE3002");
返回数据:
(2)带参的逻辑异常处理
throw ExecptionUtils.businessException("EE4001", user.getUsername());
返回数据:
(3)系统级异常处理
throw ExecptionUtils.systemException("EE9999");
返回数据:
如果你有更好的异常统一处理建议,欢迎一起讨论完善。
本文为 InfoQ 作者【xcbeyond】的原创文章。
