代码之家  ›  专栏  ›  技术社区  ›  Zombies

异常处理标准建议

  •  3
  • Zombies  · 技术社区  · 16 年前

    在J2EE应用程序中,我计划标准化异常处理策略。不再吞咽异常,基本上将所有已检查的异常转换为未检查的异常(除了一些 可以 从中恢复。标准是所有例外情况都将被抛回容器(例如: http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html )异常(如sqleexception)无法从中恢复,如果它发生在数据层的深处,我将强制它向上恢复。 然而 ,小的子例程(如电子邮件通知)将包装在一个try catch中,以防止发生的异常破坏整个应用程序流。(例如:为什么因为用户没有收到他们根本不需要的电子邮件通知而停止整个过程的完成!)

    有什么建议吗?再次重申,我基本上是将所有异常重新抛出到容器中,该容器有自己的错误页,然后将其记录到log4j中。

    5 回复  |  直到 16 年前
        1
  •  2
  •   Yishai    16 年前

    我认为电子邮件示例是对已检查异常的最佳防御。您有可能出错并且是暂时的,所以检查异常的存在会让您考虑它。如果一切都是运行时异常,那么您的应用程序只会因为一些微不足道的原因而崩溃,没有人会考虑它。

    无论如何,如果对于您的项目,一般的答案是将其抛到顶部,那么在运行时异常中包装是完全有效的。需要考虑的几个问题:

    坚持例外链接。不这样做的唯一原因是,如果您正在序列化异常,那么某些包含不可序列化成员的异常可能会使事情变得有趣。这似乎不是事实,所以不要允许例外吞咽。如果有必要,使用initcuse。下面是我们项目中的一个小助手方法:

    public static <T extends Throwable> T initCause(T newException, Throwable cause) {
        return (T) newException.initCause(cause);
    }
    

    这有助于人们避免抛出异常的需要。

    我更喜欢避免不必要的链接runtimeexception和错误throwables,当一个方法上有许多不同的已检查异常,而开发人员只是捕获异常时,不可避免地会发生这种情况,因此我建议使用一个这样的静态方法:

    public static void throwRuntimeExceptionFromThrowable(Throwable e) {
        if (e == null) {
            return;
        } else if (e instanceof RuntimeException) {
            throw (RuntimeException) e;
        } else if (e instanceof Error) {
            throw (Error) e;
        } else {
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    

    有两种选择。一种方法是使其无效(就像这段代码一样),这样调用方的好处是永远不会忘记抛出结果。缺点是如果你有这样的东西:

    public Object someMethod() {
        try {
            return getTheObject();
        } catch (Exception e) {
            throwRuntimeExceptionFromThrowable(e);
        }
    }
    

    编译器不会喜欢你的。您必须在catch之后返回,并在try之前声明一个变量。

    另一个选项是将异常作为runtimeexception返回(只抛出错误),并让调用方进行抛出。编译器更快乐,但是调用方可能忘记了这样做,并且在调用方法之前不加抛出就调用它,然后异常就被吞没了。

        2
  •  1
  •   svachon    16 年前

    异常应该由与之相关的层处理。 因为一个异常被选中并不意味着你可以处理它,或者你必须在你的应用程序的每一层处理它。此时,您可能希望将它们转换为运行时异常。因此,您可以忽略(记录)异常、无法从中恢复的异常以及可以恢复的异常。问题是相同的异常类型,例如sqlException,可以输入多个类别。IOException也是另一个例子。 无法恢复的编程错误应为运行时异常,并应在容器附近捕获以进行日志记录或使应用程序“轻微”失败。访问JDBCPreparedStatement中的无效索引是一个编程错误。 没有连接问题。因此,在数据层,您设法区分,然后将不可恢复的异常转换为运行时异常并抛出它。有时,这并不意味着重试连接会起作用。 业务方法应该只关注与业务相关的异常,这就是为什么您不在代码中的任何地方处理NullPointerExceptions或ArrayIndexOutOfBoundsException,并且不必将它们放入方法签名中的原因。同样的逻辑也适用于计算部门员工平均工资的数据源不可知的业务方法,如果用户提供了错误的部门代码,则不应抛出sqlExceptions,而应抛出invalidDeptexception等内容。 throws声明也是自文档化代码的,如果您抛出自己的运行时异常,请确保正确地文档化。

        3
  •  1
  •   anikitin    16 年前

    一般来说,我建议遵循 Fault Barrier 在您的案例中,模式是对您计划遵循的方法的进一步阐述。在这里查看: http://www.oracle.com/technology/pub/articles/dev2arch/2006/11/effective-exceptions.html (见 an older review of it )

    对于电子邮件,不确定您的具体情况。总的来说,这听起来是AOP的完美案例。如果你在Jee5,那是一个习惯 @Interceptor 可能是有序的-公共的“非中断”逻辑由于运行时异常可能被编码在那里,然后您可以将其绑定到多个bean/方法。

        4
  •  0
  •   Rich Kroll    16 年前

    对于数据库访问异常,我建议查看 Spring 处理它们。我发现这很容易在实践中使用。

        5
  •  0
  •   Matthew Flaschen    16 年前

    您从何处得到这样的想法:您永远无法从sqlexception中恢复(除其他之外)?在精心编写的代码中,您通常可以。首先,整个sqlTransientException层次结构都是针对可以从中恢复的异常(例如sqlTransientConnectionException)。当异常事实上可以处理时,将其视为不可恢复的异常是一个常见的错误。

    我认为你的“减亏”政策没有什么错,比如说电子邮件的问题。当然,应该通知用户。