Java中异常应用实践

1、问题:

不同层代码之间的异常情况,通过『返回码包装』还是『通过异常捕获』解决?

Java应用中,上层的代码会调用下层的代码:比如Controller层调用Service层,Service层调用Dao层等等。

上层代码调用下层代码会出错,比如UserServer.getUserById()这个方法,根据ID获取用户的详细信息。

2、解决方式:

错误的情况下,下层代码需要告诉上层代码错误发生了。
此时有两种通知上层调用方:
方法一:通过返回码的包装
方法二:通过抛出异常

两种详细说明如下:

2.1、 方法一:通过返回码的包装

UserServer.getUserById()返回一个Result<User>类

  • 函数定义如下:
Result<User> UserServer.getUserById(int id);
  • 其中Result定义如下:
public class Result<T> {
    private RetCodeEnum retCodeEnum;
    private T info;
}
  • 调用方调用姿势如下:
Result<User> userResult = userServer.getUserById(5);
if (RetCodeEnum.OK != userResult.getRetCodeEnum) {
    //给用户相应提示
}

//继续处理业务

2.2、方法二:通过抛出异常

UserServer.getUserById()抛出业务异常,

  • 函数定义如下:
User UserServer.getUserById(int id) throw BusinessException;
  • 其中BusinessException定义如下:
public class RetCodeEnumException extends Exception {
    private RetCodeEnum retCodeEnum;
}
  • 调用方调用姿势如下:
User user = userServer.getUserById(5);
//继续处理业务
  • 最后来一个全局的ExceptionHandler()来处理业务异常:
    protected BaseResp handleException(HttpServletRequest req, Exception e) {

        BaseResp baseResp;
        if (e instanceof BusinessException) {
            BusinessException businessException = (BusinessException) e;
            baseResp = new BaseResp(new Result(BusinessException));
        } else {
            baseResp = new BaseResp(new Result(RetCodeEnum.SYS_BUSY));
            LOGGER.error(e.getMessage(), e);
        }

        return baseResp;
    }

3、两种方式比较

第二种调用方式,把错误情况的处理统一到一个Handler处理,完爆第一种方式,理由如下:

3.1、大大减少了重复代码量。

比如你的Java应用有10层,最底层的一个函数有一种错误的情况,那上面9层调用这个方法的100个函数都要加上这么一句:

if (RetCodeEnum.OK != userResult.getRetCodeEnum) {
    //给用户相应提示
}

3.2、大大提升了安全性 && 健壮性

第一种方式,每次调用都要添加以上几行代码,而一旦忘记,就意味着 安全问题 or 莫名其妙的错误提示!

第二种方式,因为Exception的抛出是强制的,一旦错误情况发生,一定会中断正常的流程。

3.3、简化错误逻辑处理的代码

某天想在所有错误的情况下打印日志,

  • 第一种方式,需要在所有调用的地方打印日志,LOGGER.error("xxx")可能要写几千次;
  • 第二种方式,只需要在ExceptionHandler中添加一次LOGGER.error("xxx")即可。

3.4、错误信息更加 清晰结构化

如果
底层的一个方法a出错,导致方法 b、c、d 都产生问题
底层的一个方法m出错,导致方法 o、p、q 都产生问题

  • 如果用返回码的方式处理

那么日志里看起来是这样的:

a error
b error
c error
d error
m error
o error
p error
q error

很难区分到底出现了几次问题,每次出问题前因后果是什么

  • 如果用异常的方式处理

那么日志里看起来是这样的:

A Exception:
    Statck trace:
        d
        c
        b
        a
    Cause by:
        a
        
B Exception:
    Stack trace:
        q
        p
        o
        m
    Cause by:
        m

容易可以看出产生了几次问题,问题的前因后果是怎样的。

4、结论

鉴于以上种种好处,Java中错误情况的处理尽量通过 Exception 解决,而非通过返回码的包装解决。