【问题标题】:Is it a good practice to catch a variety of specific exceptions along with one generic catch-all exception?将各种特定异常与一个通用的包罗万象的异常一起捕获是一种好习惯吗?
【发布时间】:2018-10-29 17:18:12
【问题描述】:

一般问题

在编写服务方法时,抛出许多异常与将它们抽象为通用 ServiceException 相比,各有利弊。如果你抛出了很多异常,那么你告诉客户端究竟出了什么问题,以便客户端可以更明智地处理错误。如果您将它们抽象为一个ServiceException(例如通过exception wrapping),那么您就隐藏了问题的详细信息,这在某些情况下可能有用但在其他情况下有害。

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

我的具体问题

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

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

我之所以这么问,是因为我觉得捕捉这四个与验证相关的异常有点奇怪,然后又捕捉到一个通用的、不起眼的ServiceException。这似乎很奇怪,因为ServiceException 是如此抽象,以至于乍一看无法判断出了什么问题,您必须检查日志并阅读异常消息,或者导航代码中的调用层次结构。因此,我要求验证混合这两种方法确实是一种好的做法、常见的或明智的做法,因为直觉上这对我来说似乎很奇怪。核心 Java 中是否有任何相似之处,即两种异常处理方法都在同一个方法中使用?

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

在我的服务类中处理这样的异常有意义吗?下面是一些伪代码,提供了一种可能的解决方案。这样我就不会创建一个无用的、已检查的、无法描述的 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);
        }
    }
}

【问题讨论】:

  • Effective Java 的建议是抛出适合抽象的异常。
  • @AndyTurner 谢谢,这是个好建议。你投了反对票吗?如果是,为什么?
  • 我没有投反对票。
  • 虽然 JDBC API 抛出 checked SQLExceptionJdbcTemplate 包装那些带有 unchecked DataAccessException 的,所以调用者不需要专门申报和处理。无需使用ServiceException 包装DataAccessException。如果服务执行其他引发 checked 异常的操作,则应使用特定异常类似地包装这些异常,而不是通用 ServiceException。调用者并不关心这些异常,因为调用者对它们无能为力,但不要只是将所有异常包装到一个通用的 ServiceException
  • @tsolakp 但我希望调用者对它们采取行动,因为它们是可恢复的异常,所以将它们声明为已检查是有意义的,不是吗?请参阅本文末尾的十个技巧:blog.takipi.com/…

标签: java exception exception-handling


【解决方案1】:

大多数异常应该取消检查,因为它们不是真正可操作的,即调用者实际上无能为力,除了传递它,或记录它并失败当前的任何操作进行中。只应检查可操作且需要此类调用方操作的异常,这非常罕见。

如果所有异常都未检查,则意味着调用者可以使用单个捕获所有异常。由于服务层不再需要包装异常,例如在 DAO 层中,调用者可以根据需要捕获和处理特定异常。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-02
    相关资源
    最近更新 更多