企业应用程序在构建时常常对异常处理关注甚少这会造成对低级异常(如 javarmiRemoteException 和 javaxnamingNamingException)的过度依赖在 EJB 最佳实践的这篇专栏文章中Brett McLaughlin 解释了为什么对异常处理投入一点关注就会给我们带来很大的帮助并向您展示了两种简单技术它们将帮助您正确地构建更健壮且有用的异常处理框架 在本系列先前的技巧文章中异常处理不属于讨论的核心范围之内但是您可能会发现一点那就是我们一直都回避来自 Web 层的低级异常我们向客户机提供诸如 ApplicationException 和 InvalidDataException 之类的异常而没有让 Web 层处理象 javarmiRemoteException 或 javaxnamingNamingException 这样的异常 远程和命名异常是系统级异常而应用程序和非法数据异常是业务级异常因为它们提交更适用的业务信息当决定抛出何种类型的异常时您应该总是首先考虑将要处理所报告异常的层Web 层通常是由执行业务任务的最终用户驱动的所以最好用它处理业务级异常但是在 EJB 层您正在执行系统级任务如使用 JNDI 或数据库尽管这些任务最终将被合并到业务逻辑中但是最好用诸如 RemoteException 之类的系统级异常来表示它们 理论上您可以让所有 Web 层方法预期处理和响应单个应用程序异常正如我们在先前的一些示例中所做的一样但这种方法不适用于长时间运行让您的委派方法抛出更具体的异常这是一个好得多的异常处理方案从根本上讲这对接收客户机更有用在这篇技巧文章中我们将讨论两种技术它们将有助于您创建信息更丰富更具体的异常而不会生成大量不必要的代码 嵌套的异常 在设计可靠的异常处理方案时要考虑的第一件事情就是对所谓的低级或系统级异常进行抽象化这些核心 Java 异常通常会报告网络流量中的错误JNDI 或 RMI 问题或者是应用程序中的其它技术问题RemoteExceptionEJBException 和 NamingException 是企业 Java 编程中低级异常的常见例子 这些异常完全没有任何意义由 Web 层的客户机接收时尤其容易混淆如果客户机调用 purchase() 并接收到 NamingException那么它在解决这个异常时会一筹莫展同时应用程序代码可能需要访问这些异常中的信息因此不能轻易地抛弃或忽略它们 答案是提供一类更有用的异常它还包含低级异常清单 演示了一个专为这一点设计的简单 ApplicationException 清单 嵌套的异常 package comibm; import javaioPrintStream; import javaioPrintWriter; public class ApplicationException extends Exception { /** A wrapped Throwable */ protected Throwable cause; public ApplicationException() { super(Error occurred in application); } public ApplicationException(String message) { super(message); } public ApplicationException(String message Throwable cause) { super(message); thiscause = cause; } // Created to match the JDK Throwable method public Throwable initCause(Throwable cause) { thiscause = cause; return cause; } public String getMessage() { // Get this exceptions message String msg = supergetMessage(); Throwable parent = this; Throwable child; // Look for nested exceptions while((child = getNestedException(parent)) != null) { // Get the childs message String msg = childgetMessage(); // If we found a message for the child exception // we append it if (msg != null) { if (msg != null) { msg += : + msg; } else { msg = msg; } } // Any nested ApplicationException will append its own // children so we need to break out of here if (child instanceof ApplicationException) { break; } parent = child; } // Return the completed message return msg; } public void printStackTrace() { // Print the stack trace for this exception superprintStackTrace(); Throwable parent = this; Throwable child; // Print the stack trace for each nested exception while((child = getNestedException(parent)) != null) { if (child != null) { Systemerrprint(Caused by: ); childprintStackTrace(); if (child instanceof ApplicationException) { break; } parent = child; } } } public void printStackTrace(PrintStream s) { // Print the stack trace for this exception superprintStackTrace(s); Throwable parent = this; Throwable child; // Print the stack trace for each nested exception while((child = getNestedException(parent)) != null) { if (child != null) { sprint(Caused by: ); childprintStackTrace(s); if (child instanceof ApplicationException) { break; } parent = child; } } } public void printStackTrace(PrintWriter w) { // Print the stack trace for this exception superprintStackTrace(w); Throwable parent = this; Throwable child; // Print the stack trace for each nested exception while((child = getNestedException(parent)) != null) { if (child != null) { wprint(Caused by: ); childprintStackTrace(w); if (child instanceof ApplicationException) { break; } parent = child; } } } public Throwable getCause() { return cause; } } 清单 中的代码很简单我们已经简单地将多个异常串在一起以创建单个嵌套的异常但是真正的好处在于将这种技术作为出发点以创建特定于应用程序的异常层次结构异常层次结构将允许 EJB 客户机既接收特定于业务的异常也接收特定于系统的信息而不需要编写大量额外代码 异常层次结构 异常层次结构应该从一些十分健壮而又通用的异常入手如 ApplicationException如果您将顶级异常搞得太具体那么其结果是您今后将不得不重新构造层次结构以适应某些较通用的情况 因此让我们假定您的应用程序要求 NoSuchBookExceptionInsufficientFundsException 和 SystemUnavailableException您不必创建这三个异常让它们继承 ApplicationException然后只需提供极少几个必须的构造器来创建格式化的消息清单 是此类异常层次结构的示例 清单 异常层次结构 package comibmlibrary; import comibmApplicationException; public class NoSuchBookException extends ApplicationException { public NoSuchBookException(String bookName String libraryName) { super(The book + bookName + was not found in the + libraryName + library); } } 当需要编写大量专用异常时异常层次结构极大地简化了工作对于一个异常为每个异常类添加一个或两个构造器所花费时间很少不超过几分钟您还经常需要给这些更具体的异常(这些异常也是主应用程序异常的子类)提供子类以提供更具体的异常例如您可能需要 InvalidTitleException 和 BackorderedException 来继承 NoSuchBookException 企业应用程序在构建时通常都不会注意异常处理尽管依靠低级异常(如 RemoteException 和 NamingException)很容易(有时也很诱人)但如果一开始就建立一个可靠的深思熟虑的异常模型则您将在应用程序上少花很多精力创建一个嵌套的层次结构化的异常框架将改进代码的可读性及其可用性 |