如何从未经检查的异常中恢复? [英] How do I recover from an unchecked exception?

查看:46
本文介绍了如何从未经检查的异常中恢复?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果您想以相同的方式处理每个失败,则未检查的异常是可以的,例如通过记录它并跳到下一个请求,向用户显示一条消息并处理下一个事件等.如果这是我的用例,我所要做的就是在我的系统中在高层捕获一些通用的异常类型,并以相同的方式处理所有事情.

Unchecked exceptions are alright if you want to handle every failure the same way, for example by logging it and skipping to the next request, displaying a message to the user and handling the next event, etc. If this is my use case, all I have to do is catch some general exception type at a high level in my system, and handle everything the same way.

但我想从特定问题中恢复过来,我不确定处理未经检查的异常的最佳方法.这是一个具体的例子.

But I want to recover from specific problems, and I'm not sure the best way to approach it with unchecked exceptions. Here is a concrete example.

假设我有一个使用 Struts2 和 Hibernate 构建的 Web 应用程序.如果异常冒泡到我的操作",我会记录它,并向用户表示非常抱歉.但是我的 Web 应用程序的功能之一是创建新的用户帐户,这需要一个唯一的用户名.如果用户选择了一个已经存在的名称,Hibernate 会在我的系统内部抛出一个 org.hibernate.exception.ConstraintViolationException(一个未经检查的异常).我真的很想通过要求用户选择另一个用户名来解决这个特定问题,而不是给他们同样的我们记录了你的问题,但现在你已经被灌输"的消息.

Suppose I have a web application, built using Struts2 and Hibernate. If an exception bubbles up to my "action", I log it, and display a pretty apology to the user. But one of the functions of my web application is creating new user accounts, that require a unique user name. If a user picks a name that already exists, Hibernate throws an org.hibernate.exception.ConstraintViolationException (an unchecked exception) down in the guts of my system. I'd really like to recover from this particular problem by asking the user to choose another user name, rather than giving them the same "we logged your problem but for now you're hosed" message.

这里有几点需要考虑:

  1. 有很多人同时创建帐户.我不想在SELECT"之间锁定整个用户表以查看名称是否存在,如果不存在则锁定INSERT".在关系数据库的情况下,可能有一些技巧可以解决这个问题,但我真正感兴趣的是一般情况下,由于基本的竞争条件,对异常的预检查不起作用.同样的事情也适用于在文件系统上查找文件等.
  2. 鉴于我的 CTO 倾向于通过阅读Inc."中的技术专栏而导致的驱动管理,我需要一个围绕持久性机制的间接层,以便我可以抛弃 Hibernate 并使用 Kodo 或其他任何东西,而无需更改任何内容除了最底层的持久化代码.事实上,在我的系统中有几个这样的抽象层.尽管有未经检查的异常,我如何才能防止它们泄漏?
  3. 已声明的受检异常的弱点之一是必须在堆栈上的每次调用中处理"它们——要么通过声明调用方法抛出它们,要么通过捕获它们并处理它们.处理它们通常意味着将它们包装在另一个适合抽象级别的类型的已检查异常中.因此,例如,在检查异常领域,我的 UserRegistry 的基于文件系统的实现可能会捕获 IOException,而数据库实现会捕获 SQLException,但两者都会抛出一个隐藏底层实现的 UserNotFoundException.我如何利用未经检查的异常,避免在每一层进行这种包装的负担,同时又不泄露实现细节?
  1. There a lot of people creating accounts simultaneously. I don't want to lock the whole user table between a "SELECT" to see if the name exists and an "INSERT" if it doesn't. In the case of relational databases, there might be some tricks to work around this, but what I'm really interested in is the general case where pre-checking for an exception won't work because of a fundamental race condition. Same thing could apply to looking for a file on the file system, etc.
  2. Given my CTO's propensity for drive-by management induced by reading technology columns in "Inc.", I need a layer of indirection around the persistence mechanism so that I can throw out Hibernate and use Kodo, or whatever, without changing anything except the lowest layer of persistence code. As a matter of fact, there are several such layers of abstraction in my system. How can I prevent them from leaking in spite of unchecked exceptions?
  3. One of the declaimed weaknesses of checked exceptions is having to "handle" them in every call on the stack—either by declaring that a calling method throws them, or by catching them and handling them. Handling them often means wrapping them in another checked exception of a type appropriate to the level of abstraction. So, for example, in checked-exception land, a file-system–based implementation of my UserRegistry might catch IOException, while a database implementation would catch SQLException, but both would throw a UserNotFoundException that hides the underlying implementation. How do I take advantage of unchecked exceptions, sparing myself of the burden of this wrapping at each layer, without leaking implementation details?

推荐答案

IMO,包装异常(已检查或以其他方式)有几个值得付出代价的好处:

IMO, wrapping exceptions (checked or otherwise) has several benefits that are worth the cost:

1) 它鼓励您考虑您编写的代码的失败模式.基本上,您必须考虑您调用的代码可能抛出的异常,然后您将考虑为调用您的代码而抛出的异常.

1) It encourages you to think about the failure modes for the code you write. Basically, you have to consider the exceptions that the code you call may throw, and in turn you'll consider the exceptions you'll throw for the code that calls yours.

2) 它让您有机会将额外的调试信息添加到异常链中.例如,如果您有一个对重复用户名引发异常的方法,您可以用一个包含有关失败情况的附加信息(例如,提供重复用户名的请求的 IP)的方法来包装该异常对低级代码不可用.异常的 cookie 跟踪可能会帮助您调试一个复杂的问题(这对我来说肯定有).

2) It gives you the opportunity to add additional debugging information into the exception chain. For instance, if you have a method that throws an exception on a duplicate username, you might wrap that exception with one that includes additional information about the circumstances of the failure (for example, the IP of the request that provided the dupe username) that wasn't available to the lower-level code. The cookie trail of exceptions may help you debug a complex problem (it certainly has for me).

3) 它使您可以独立于较低级别的代码实现.如果您正在包装异常并且需要将 Hibernate 换成其他一些 ORM,您只需更改您的 Hibernate 处理代码.所有其他代码层仍将成功使用包装的异常,并以相同的方式解释它们,即使底层环境已更改.请注意,即使 Hibernate 以某种方式更改(例如:它们在新版本中切换异常),这也适用;这不仅仅是为了批发技术更换.

3) It lets you become implementation-independent from the lower level code. If you're wrapping exceptions and need to swap out Hibernate for some other ORM, you only have to change your Hibernate-handling code. All the other layers of code will still be successfully using the wrapped exceptions and will interpret them in the same way, even though the underlying circumstances have changed. Note that this applies even if Hibernate changes in some way (ex: they switch exceptions in a new version); it's not just for wholesale technology replacement.

4) 它鼓励您使用不同类别的异常来表示不同的情况.例如,当用户尝试重用用户名时,您可能会遇到 DuplicateUsernameException,而当您由于数据库连接断开而无法检查重复用户名时,您可能会遇到 DatabaseFailureException.反过来,这可以让您以灵活而强大的方式回答您的问题(我如何恢复?").如果您收到 DuplicateUsernameException,您可以决定向用户建议不同的用户名.如果您收到 DatabaseFailureException,您可以让它冒泡,直到它向用户显示停机维护"页面并向您发送通知电子邮件.一旦您有了自定义例外,您就有了可自定义的响应——这是一件好事.

4) It encourages you use different classes of exceptions to represent different situations. For example, you may have a DuplicateUsernameException when the user tries to reuse a username, and a DatabaseFailureException when you can't check for dupe usernames due to a broken DB connection. This, in turn, lets you answer your question ("how do I recover?") in flexible and powerful ways. If you get a DuplicateUsernameException, you may decide to suggest a different username to the user. If you get a DatabaseFailureException, you may let it bubble up to the point where it displays a "down for maintenance" page to the user and send off a notification email to you. Once you have custom exceptions, you have customizeable responses -- and that's a good thing.

这篇关于如何从未经检查的异常中恢复?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆