我对中型Java Web应用程序中的异常有疑问。 有一个使用JDBC实现的数据访问层,逻辑主要集中在servlet中(UI是JSP)。对于像这样的应用程序,常规异常的层次结构是什么?
我应该捕获数据访问层上的异常并重新抛出整个异常(例如DataAccessException),或者只让最高级别处理它们(servlet)。
此外,我有一个在数据访问层内调用的连接池,它有自己的异常类型。这些异常是应该在数据访问层内捕获并作为DataAccessException重新抛出,还是应该由更高级别直接处理?
与2个子节点一起使用主应用程序异常是一个好主意:LogicException和TechnicalException。逻辑将具有类似于AuthentificationFailedException等的子类,而TechnicalExceptions将负责传递有关故障的信息,如数据访问层异常,FileNotFound(应该是)等等?
谢谢!
答案 0 :(得分:2)
在应用程序中我已经看到所有异常都被引入它们的层中,包含在一个更通用的异常中(在你的情况下是DataAccessException),然后以这种方式重新抛出。 这是因为通常引发异常的层没有足够的关于上下文的信息,因此它无法以适当的方式决定如何处理错误。处理这些异常的最佳位置是位于抛出异常的图层右侧的图层:它具有足够的信息以及#34;优雅地失败"不要让那个异常在堆栈上走得太远。 在您的情况下,捕获servlet中的异常将完成工作。
但例外的层次结构取决于您正在处理的应用程序。您的逻辑技术部门可能是一个不错的选择......或者可能不是。 :)没有"对"选择"异常处理",并且在单个Q& A对中处理这个问题太复杂了。 :)
答案 1 :(得分: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
}
然后我必须更改所有客户端代码来处理IOException
而不是SQLException
s。如果你进行包装,你只需要更改后端,因为你的客户端代码已经编写了ConfigurationException
s及其子类。请注意,这与使用已检查或未检查的异常无关:如果要进行异常处理,则几乎总是需要至少知道要处理的异常类型的近似值。
现在,这是我倾向于做的事情。有些人认为大多数例外无论如何都无法妥善处理,所有这些包装在大多数情况下都是无意义的。
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;
}
}
答案 2 :(得分:2)
通常,在应用程序的任何给定层中,您希望捕获的异常对该层有意义。因此,您不希望在Servlet(或MVC控制器)中捕获SybaseException,因为可能您有一个DAO层来隐藏该级别的实现细节。
情侣一般提示:
最后,并不是每个人都同意这一点,但你可以强有力地将所有异常子类化为RuntimeException,以便取消选中它们。我已经研究过许多使用这种方法的大型代码库,它通常工作正常,并且减少了大量的catch-rethrow样板代码。仅仅因为java提供了已检查的异常,并不意味着您必须使用它们。这显然是我的意见,但重要的是要意识到不是每个人都使用已检查的异常,事实上,许多代码库都会明确地避免它们。
答案 3 :(得分:1)
我也参与开发中型Web应用程序。我们使用的方法是将数据库层中的异常抛出到处理捕获它们的逻辑的servlet,并向用户生成正确的错误消息。
这是.... 不去IMO的方式。 原因:整个servlet现在依赖于实现。假设我们想要切换实现(我们现在想要的)。我们现在需要更改servet其余部分中的每个catch,因为新实现不会抛出相同的异常。通用异常是正确的选择。
不仅例外而且业务对象也应该是通用的。优选接口。这将允许您更轻松地更改实现,而您的locig处理代码将通过您的servlet整个生命周期依赖于相同的对象。