服务层是否应该捕获所有dao异常并将其包装为服务异常?


23

我有三层Spring Web应用程序:dao,服务和控制器。控制器从不直接调用dao,而是通过服务层进行调用。现在,大多数情况下,如果存在未处理的dao异常(运行时),则JSP将捕获该异常,并向最终用户显示错误消息。服务层是否应该捕获所有dao异常并将其包装为服务异常?

try {
   daoInstance.someDaoMethod();
} catch(DataAccessException dae) {
   throw new ServiceException("message", dae);
}

假设ServiceException也是运行时,并且也未处理。仅抛出DataAccessException而不是ServiceException有什么区别吗?我只是以为表示层不应该知道数据访问异常。但是我不认为捕获不可恢复的异常只是为了包装它们。

Answers:


18

我认为一个重要因素是您的服务客户是谁。

如果您的服务层只是您自己项目中各层之间的架构边界,并且服务客户端位于同一信任域内,则可以放轻松,让未经检查的异常冒泡到控制器层或服务客户端。

但是,对于面向公众的代码;由第三方或客户使用的服务,我认为将所有未经检查的异常与面向服务的异常包装在一起是比较干净的,主要是出于安全方面的考虑,其次是松散耦合和干净的抽象。

数据层异常永远都不应直接使之一直困扰Web应用程序的最终用户。它可能包含有关架构,查询,行号信息,变量或函数名等的内部信息。可以在安全设置中清除最终用户异常。

外部服务客户端不关心您的实现细节,也无法处理未经检查的异常,因为它们是错误或环境问题。在安全的应用程序中,数据库错误根本不够安全,无法传播,OracleException - ORA-01234 - ...这可能是插入的第三个表。应该允许客户端处理它可以处理的所有已检查/预期的异常,并将其他所有内容视为潜在的错误报告。您的服务合同应该是原子的,一致的事务性抽象。如果它对异常无能为力,那么剩下的唯一有用的事情就是给您一个错误报告。。您已经具有记录异常的功能,那么为什么要给最终用户增加详细信息呢?您的应用可以受到监控,因此在用户报告未检查的异常之前,您已经知道它们。

绝对不能异常,我也不喜欢检查异常,但我更喜欢制定适合整个产品性质的计划。


13

不,您不应将DAO异常包装在Web应用程序中

代码中存在很多杂音,导致零收益。DAO异常是未经检查的异常,这是有充分理由的。应用程序代码无法做任何有用的事情来从DAO异常中恢复。真正的问题在这里:

...它会被向最终用户显示错误消息的JSP捕获。

在一处解决此问题,而不是浪费整个代码库。

您可以控制未捕获的异常如何显示给用户。未捕获的异常是由于应用程序错误或底层系统故障引起的。没有理由向用户提供有关无法满足其请求的原因的任何信息。用户无能为力。您只需要提供一个友好的错误页面即可。

注意:如果您发现自己编写了大量繁琐的代码,例如,创建一堆简单包装并重新抛出的catch块,则几乎总是有更好的解决方案。


感谢您的回答。是的,jsp显示了一条自定义消息:“糟糕,出现了问题”。它没有显示有关该异常的任何信息
奥斯卡

7

使用异常包装的主要原因是防止业务层中的代码必须了解系统中每种可能的异常。这有两个主要原因:

  • 一致性:声明的异常聚集在调用堆栈的顶部。如果您不包装异常,而是通过声明要抛出的方法来传递它们,则最终可能会使用声明许多不同异常的顶级方法。在每个方法中声明所有这些异常以备份调用堆栈变得乏味。

  • 封装:您可能不希望顶层组件对底层组件有任何了解,也不希望它们抛出的异常。例如,DAO接口和实现的目的是从应用程序的其余部分抽象数据访问的详细信息。现在,如果您的DAO方法抛出SQLException,则使用DAO的代码将必须捕获它们。如果更改为从Web服务而不是从数据库读取数据的实现,该怎么办?然后,您的DAO方法将必须同时抛出RemoteException和SQLException。而且,如果您有一个DAO可以从文件中读取数据,则也需要抛出IOException。那是三个不同的例外,每个例外都绑定到自己的DAO实现。

简而言之,答案是肯定的!


3
JPA实现(例如Hibernate)抛出未经检查的异常。它们不必被声明或捕获。
凯文·克莱恩
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.