捕获各种特定异常以及一个通用的通用捕获异常是一种好习惯吗?

时间:2018-10-29 17:18:12

标签: java exception exception-handling

一般问题

编写服务方法时,引发许多异常与将其抽象为通用ServiceException相比有其优点和缺点。如果抛出许多异常,则可以告诉客户端确切的错误原因,以便客户端可以更明智地处理错误。如果将它们抽象为一个ServiceException(例如,通过exception wrapping),那么您将隐藏发生错误的详细信息,这在某些情况下可能有用,而在其他情况下却是有害的。

例如,通过抛出泛型DaoException来隐藏DAO类中出错的细节可能很有用。这样,客户端不必知道您是从文件,Web服务还是从关系数据库中进行读取。但是,假设您正在验证用户输入,并且至少有四件事可能会导致用户输入出错。在后一种情况下抛出四个不同的异常不是更明智,对于验证出错的每一件事都抛出一个异常?

我的具体问题

在我当前的项目中,我正在研究一种服务方法,该方法将树状结构保存到关系数据库中。它必须验证树,并且至少有四件事可能与树有关。可能存在重复的节点,或者节点可能缺少其必填的X,Y,Z字段。因此,服务方法抛出四个不同的异常是有意义的:DuplicateNodeExceptionMissingXExceptionMissingYExceptionMissingZException

但是服务方法还使用DAO类,该类通过JdbcTemplate抛出DataAccessException。我想知道如何处理此DataAccessException。将其包装为ManagerException,然后再次包装为ServiceException,并作为ServiceException处理,是否有意义?然后,我将混合两种方法(a)抛出特定的描述性异常,以及(b)抛出通用包装异常。在这里混合使用这些方法是一个好主意吗?

我问的原因是,对于我来说,捕获这四个与验证相关的异常,然后捕获通用的,非描述性的ServiceException,似乎有点奇怪。似乎很奇怪,因为ServiceException太抽象了,以至于无法一目了然地知道出了什么问题,您将不得不检查日志并阅读异常消息,或者浏览代码中的调用层次结构。然后,我要求验证将这两种方法混合使用确实是一种好习惯,或者是普遍的,还是明智的,因为从直觉上看,这对我来说很奇怪。在核心Java中是否有任何相似之处,其中两种异常处理方法都在同一方法中使用?

可能的解决方案(根据Andreas的回答)

在Service类中处理此类异常是否有意义?以下是一些伪代码,提出了一种可能的解决方案。这样,我不会创建无用的,已检查的,非描述性的ServiceException,但是我确实具有未检查的异常的全部捕获(通过在末尾捕获Exception)。您对此解决方案有何看法?

class HttpService {

    private Service service;

    // ...

    public HttpServiceResponse saveTree(Node root) {
        try {
            service.saveTree(root);  
        } catch (DuplicateNodeException e) {
            return HttpServiceResponse.failure(DUPLICATE_NODE_EXCEPTION);
        } catch (MissingXException e) {
            return HttpServiceResponse.failure(MISSING_X_EXCEPTION);
        } catch (MissingYException e) {
            return HttpServiceResponse.failure(MISSING_Y_EXCEPTION);
        } catch (MissingZException e) {
            return HttpServiceResponse.failure(MISSING_Z_EXCEPTION);
        } catch (Exception e) {
            return HttpServiceResponse.failure(INTERNAL_SERVER_ERROR);
        }
    }
}

1 个答案:

答案 0 :(得分:3)

大多数异常应该是未经检查的,因为它们实际上不是可操作的,即调用者除了传递,记录或使当前执行的任何操作失败之外,实际上无能为力进行中。仅应检查可操作且需要此类调用者操作的异常,这种情况很少见。

如果所有异常均未选中,则表示调用方可以处理单个捕获。由于服务层不再需要包装来自例如在DAO层中,如果需要,调用方 可以捕获并处理特定的异常。