3

我有一个关于中型 Java Web 应用程序中的异常的问题。有一个使用 JDBC 实现的数据访问层,其逻辑主要集中在一个 servlet 中(UI 是 JSP)。像这样的应用程序的常规异常层次结构是什么?

我应该在数据访问层捕获异常并重新抛出另一个异常(例如DataAccessException)还是让最高级别处理它们(servlet)。

此外,我有一个在数据访问层中调用的连接池,它有自己的异常类型。这些异常应该在数据访问层内部捕获并作为DataAccessException重新抛出,还是应该由更高级别直接处理?

有两个孩子的主要应用程序异常是个好主意:LogicException 和 TechnicalException。Logic 将具有类似于 AuthentificationFailedException 等的子类,而 TechnicalExceptions 将负责传达有关失败的信息,例如数据访问层异常、FileNotFound(应该是)等等?

谢谢!

4

4 回答 4

2

通常,在应用程序的任何给定层中,您都希望捕获的异常对该层有意义。因此,您不希望在 Servlet(或 MVC 控制器)中捕获 SybaseException,因为您可能有一个 DAO 层来隐藏该级别的实现细节。

夫妇一般提示:

  • 您应该配置您的 apache+servlet 容器以在 500 错误时返回一个合理的页面。
  • 您的控制器应该使用用户友好的消息来包装大多数错误。捕获的异常应该为控制器提供足够的信息来制定合理的消息。
  • 我会考虑使用基本应用程序异常子类化 RuntimeException,它既包含技术错误消息,也包含本地化的用户错误消息。
  • 想想当你捕捉到异常时你想做什么设计您的例外,以便可以直接执行您需要的操作。

最后,并不是每个人都会同意这一点,但是您可以提出一个强有力的理由,让您的所有异常都成为 RuntimeException 的子类,这样它们就不会被检查。我已经在许多采用这种方法的大型代码库上工作过,它通常工作得很好,并且减少了很多 catch-rethrow 样板代码。仅仅因为 java 提供了检查异常,并不意味着你必须使用它们。这显然是我的观点,但重要的是要意识到并非每个人都使用检查异常,事实上,许多代码库会明确避免它们。

于 2012-05-14T20:34:50.067 回答
2

在应用程序中,我看到所有异常都在抛出它们的层中被捕获,包装在更通用的异常中(在您的情况下为 DataAccessException),然后以这种方式重新抛出。这是因为通常引发异常的层没有足够的上下文信息,因此无法决定如何以适当的方式处理错误。处理这些异常的最佳位置是位于引发异常的层的正上方的层:它有足够的信息来“优雅地失败”,而不会让该异常在堆栈跟踪上走得太远。在您的情况下,在 servlet 中捕获异常就可以了。

但是异常的层次结构取决于您正在处理的应用程序。您的逻辑技术部门可能是一个不错的选择……也可能不是。:) 在“异常处理”中没有“正确”的选择,而且这是一个过于复杂的论点,无法在单个问答对中处理。:)

于 2012-05-14T20:05:30.863 回答
2

通常,我用更高级别、更有意义的异常来包装较低级别的异常。这通常需要更多的工作,但可以让您在层之间解耦。

想象一下,我正在编写一个恰好从数据库读取的配置子系统。如果我不包装,我会有类似的东西:

public String getConfigurationProperty(String name) throws SQLException {
    // Try to read from my configuration table
}

如果我做包装,我会

public String getConfigurationProperty(String name) throws ConfigurationException {
    try {
        // Try to read from my configuration table 
    } catch (SQLException ex) {
        ConfigurationException wrapper = // Some subclass of ConfigurationException that wraps ex
        throw wrapper;
    }
}

这肯定是更多的工作。优点是,如果以后我想将我的配置后端更改为基于文件的后端,没有包装器,我的方法将变为

public String getConfigurationProperty(String name) throws IOException {
    // Try to read from my configuration file
}

然后我将不得不更改我的所有客户端代码以处理IOExceptions 而不是SQLExceptions。如果您进行包装,您只需要更改后端,因为您的客户端代码已经在编写时考虑了ConfigurationExceptions 及其子类。请注意,这与您使用已检查或未检查的异常无关:如果您想要进行异常处理,您几乎总是需要至少知道要处理的异常类型的一些近似值。

现在,这就是我倾向于做的事情。有人认为无论如何都无法正确处理大多数异常,并且大多数情况下所有这些包装都是无稽之谈。

public String getConfigurationProperty(String name) throws ConfigurationException {
    try {
        // Try to read from my configuration file 
    } catch (IOException ex) {
        ConfigurationException wrapper = // Some subclass of ConfigurationException that wraps ex
        throw wrapper;
    }
}
于 2012-05-14T20:28:52.090 回答
1

我还参与开发一个中等规模的 Web 应用程序。我们使用的方法是将异常从数据库层抛出到处理逻辑的 servlet,该逻辑捕获它们并向用户生成正确的错误消息。

这是....不是国际海事组织的方式。原因:整个 servlet 现在都依赖于实现。假设我们想要切换实现(我们现在想要)。我们现在需要更改服务集其余部分中的每个捕获,因为新实现不会抛出相同的异常。通用异常是正确的选择。

不仅例外,业务对象也应该是通用的。最好是接口。这将允许您更轻松地更改实现,而您的 locig 处理代码将在您的 servlet 整个生命周期中依赖于相同的对象。

于 2012-05-14T20:09:44.730 回答