我能用异常及全局异常控制来设计异常返回的流程吗? Spring MVC

2019-07-05 11:00:12 +08:00
 huangdaxian

正常来说,如果一个 Service 因为数据验证未通过等原因,需要驳回请求,则只能多层 return error,然后在 Controller 里依据某些约定来返回对应的报错信息。

我现在是通过全局异常处理来统一返回。在 Service 里需要中断请求、报错返回的情况下,直接抛出一个 RuntimeException,然后被全局处理捕获,按约定统一返回。

一直以来,我都拒绝第一种方式,因为第一种方式需要冗杂的代码来进行返回的控制,并且难以达到最好的效果。 但是我今天在阅读 [阿里巴巴 Java 开发手册时] ,上面标注了:

[强制] 异常不要用来做流程控制,条件控制。 说明:异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式要低很多。

那么该如何抉择?

如果抛出预加载的静态异常对象呢?

6404 次点击
所在节点    Java
39 条回复
Amayadream
2019-07-05 14:04:57 +08:00
不要用异常做业务流程控制, 特别是 RuntimeException, 比如用户不存在返回 UserNotFoundException, 密码不正确返回 PasswordErrorException, 用户不存在和密码不正确都属于可预料的情况, 需要业务自己处理而不是过度依赖抛异常来统一处理

如果 service 返回结果很复杂, 可以考虑声明一个复合返回类型, 类似接口层返回结果, 将可能产生的结果包装在内, 调用者看一下返回值就能知道所有可能的情况, 而不是去阅读你的代码才能知道会有多少个分支

异常做流程控制一时爽, 后续维护和调用者就要骂娘了
no1xsyzy
2019-07-05 14:46:51 +08:00
参考一下 erlang 的思想,出现 Exception 的时候应该直接认为程序无法继续,自身已经无法处理的情况。
大致上就是当作 Exception 的处理都是直接记录并重启。
chocotan
2019-07-05 14:57:47 +08:00
阿里的这个所谓规范看看就行了
之前围观 spring security 的时候看到就是用异常来控制的
wysnylc
2019-07-05 14:59:03 +08:00
异常不要用来做流程控制,条件控制。 应当改为 不要用异常来控制程序的正常流程
https://www.iteye.com/topic/857443 这是一个 8 年前的讨论,时至今日该问题还是在重复
zzxzzxhao
2019-07-05 15:43:08 +08:00
@Amayadream 我赞同你说的,不要过度依赖统一处理异常。以前我也写过全局处理异常的,但是实际业务中的抛出的异常不一定能考虑到,那就代表无法精准定位问题,反而耗费更多的精力
limuyan44
2019-07-05 15:54:53 +08:00
异常流程一种是直接 over 的流程,一种是分支流程,比如 savepoint ?合适才是最好了没必要一刀切。
liuhuansir
2019-07-05 16:06:30 +08:00
我一直都是用全局捕获自定义异常,但是有个问题请教大家,因为某些 service 中的业务逻辑是共用的,导致返回给客户端的提示信息描述不太合适,有没有好的办法?
MotherShip
2019-07-05 16:25:48 +08:00
你看看上面那条

1. [强制] Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通过
catch 的方式来处理,比如:NullPointerException,IndexOutOfBoundsException 等等。
说明:无法通过预检查的异常除外,比如,在解析字符串形式的数字时,不得不通过 catch
NumberFormatException 来实现。
正例:if (obj != null) {...}
反例:try { obj.method(); } catch (NullPointerException e) {…}


2. [强制] 异常不要用来做流程控制,条件控制。
说明:异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式
要低很多。

第二条其实和第一条的意思是一样的,能判空的情况就不要去 catch NPE,类似的还有能判断 List 长度就不要去 catch 下标越界异常。。
huangdaxian
2019-07-07 16:00:18 +08:00
@liuhuansir 异常类里定义 errorCode,使用中间层或者前端维护 message。
YzSama
2019-07-08 09:07:58 +08:00
我的做法是

1. 全局定义统一异常处理并封装异常信息。
2. 使用 HttpStatus 来做 顶层异常类,例如 401 无权限、400 参数错误或其他、404 资源找不到。包装的时候,直接就包装这类异常。
3. 业务异常里,使用 Code、Message 来处理。

例如:

```json

{
"status": 401,
"error": "Unauthorized",
"message": "用户未登录",
"code": 4001,
"path": "/example/example-xjaldkskskal",
"exception": "com.example.exception.UserNotLoginException",
"errors": [
{
"code": 20009,
"message": "token 为空"
}
],
"timestamp": 1556536629108
}

```

errors 是具体的业务错误信息。errors 包含多个 error。

我们采用了 Restful api 设计,所以使用 httpstatus 来做顶层的异常类,为了更好的针对业务异常进一步的处理和展现,就加了一个 errors。。 只有两层。

个人理解和想法。
YzSama
2019-07-08 09:18:17 +08:00
对了,我认为异常来控制流程 这一边

因为,我认为流程一旦出错,分两种,可执行 和 不可执行。

可执行,是即使出错了,也无所谓。

不可执行,是一旦数据校验不正确,不可能走下去,应该往外抛。

正常流程,是啥事都没有,这就是正确的并且是按照 研发人员设计的流程走下去的。

如果,全局异常处理的设计去考虑性能问题,我觉得对,也不对。你想想,你用的大部分第三方依赖库,它们不也往外抛异常吗?
huangdaxian
2019-07-08 12:07:38 +08:00
@YzSama 大体流程相同,也是包装 NotFoundException 这种。但是 errors 这里没看懂。errors 里面是 Exception 的错误链吗?你封装的 NotFoundException 不是 RuntimeException ?
YzSama
2019-07-08 15:19:45 +08:00
@huangdaxian #32

errors 可以理解具体错误的行为表现。

if(Objects.isNull(token)){
// result 就是 Errors 的体现错误的信息
throws new NotFoundException(ErrorResult.TOKEN_IS_NULL);
}

所以,增加了 NotFoundException(ErrorResult errorResult) ,来体现 业务主要错误的具体信息。

所有自定义异常都是继承 RuntimeException。
huangdaxian
2019-07-08 15:35:11 +08:00
@YzSama 那么什么情况下会有多个 eroor 的 eroors ? code 为 4001 的 Unauthorized error 是在哪一层被添加上的?
huangdaxian
2019-07-08 15:40:00 +08:00
@chengzh 16 楼没有看到 v
YzSama
2019-07-08 16:09:15 +08:00
@huangdaxian

封装自定义异常初始化的时候,默认生成。

errors 一般没有多个 error 的存在。

主要是方便后面拓展的时候,客户端不用从 JsonObject 改成 JsonArray。 直接就是用 JsonArray。哈哈
huangdaxian
2019-07-08 16:17:37 +08:00
@YzSama 眼看着一个饶有创造的设计揭开神秘的面纱后让我吐血满地
YzSama
2019-07-08 17:01:27 +08:00
@huangdaxian #37 😂...
ourslay
2019-07-08 17:24:32 +08:00
继承 DefaultErrorAttributes

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/580229

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX