选择显示字体大小

构建更好的异常处理框架

企业应用程序在构建时常常对异常处理关注甚少,这会造成对低级异常(如 java.rmi.remoteexception 和 javax.naming.namingexception)的过度依赖。在 ejb 最佳实践的这篇专栏文章中,brett mclaughlin 解释了为什么对异常处理投入一点关注就会给我们带来很大的帮助,并向您展示了两种简单技术,它们将帮助您正确地构建更健壮且有用的异常处理框架

在本系列先前的技巧文章中,异常处理不属于讨论的核心范围之内。但是,您可能会发现一点,那就是我们一直都回避来自 web 层的低级异常。我们向客户机提供诸如 applicationexception 和 invaliddataexception 之类的异常,而没有让 web 层处理象 java.rmi.remoteexception 或 javax.naming.namingexception 这样的异常。

远程和命名异常是系统级异常,而应用程序和非法数据异常是业务级异常,因为它们提交更适用的业务信息。当决定抛出何种类型的异常时,您应该总是首先考虑将要处理所报告异常的层。web 层通常是由执行业务任务的最终用户驱动的,所以最好用它处理业务级异常。但是,在 ejb 层,您正在执行系统级任务,如使用 jndi 或数据库。尽管这些任务最终将被合并到业务逻辑中,但是最好用诸如 remoteexception 之类的系统级异常来表示它们。

理论上,您可以让所有 web 层方法预期处理和响应单个应用程序异常,正如我们在先前的一些示例中所做的一样。但这种方法不适用于长时间运行。让您的委派方法抛出更具体的异常,这是一个好得多的异常处理方案,从根本上讲,这对接收客户机更有用。在这篇技巧文章中,我们将讨论两种技术,它们将有助于您创建信息更丰富、更具体的异常,而不会生成大量不必要的代码。

嵌套的异常
在设计可靠的异常处理方案时,要考虑的第一件事情就是对所谓的低级或系统级异常进行抽象化。这些核心 java 异常通常会报告网络流量中的错误、jndi 或 rmi 问题,或者是应用程序中的其它技术问题。remoteexception、ejbexception 和 namingexception 是企业 java 编程中低级异常的常见例子。

这些异常完全没有任何意义,由 web 层的客户机接收时尤其容易混淆。如果客户机调用 purchase() 并接收到 namingexception,那么它在解决这个异常时会一筹莫展。同时,应用程序代码可能需要访问这些异常中的信息,因此不能轻易地抛弃或忽略它们。

答案是提供一类更有用的异常,它还包含低级异常。清单 1 演示了一个专为这一点设计的简单 applicationexception:

清单 1. 嵌套的异常 package com.ibm;

import java.io.printstream;
import java.io.printwriter;
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);
           this.cause = cause;
       }
       // created to match the jdk 1.4 throwable method.
       public throwable initcause(throwable cause)  {
           this.cause = cause;
           return cause;
       }
       public string getmessage() {
           // get this exception's message.
           string msg = super.getmessage();
           throwable parent = this;
           throwable child;
           // look for nested exceptions.
           while((child = getnestedexception(parent)) != null) {
               // get the child's message.
               string msg2 = child.getmessage();
               // if we found a message for the child exception,
               // we append it.
               if (msg2 != null) {
                   if (msg != null) {
                       msg += ": " + msg2;
                   } else {
                       msg = msg2;
                   }
               }
               // 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.
           super.printstacktrace();
           throwable parent = this;
           throwable child;
           // print the stack trace for each nested exception.
           while((child = getnestedexception(parent)) != null) {
               if (child != null) {
                   system.err.print("caused by: ");
                   child.printstacktrace();
                   if (child instanceof applicationexception) {
                       break;
                   }
                   parent = child;
               }
           }
       }
       public void printstacktrace(printstream s) {
           // print the stack trace for this exception.
           super.printstacktrace(s);
           throwable parent = this;
           throwable child;
           // print the stack trace for each nested exception.
           while((child = getnestedexception(parent)) != null) {
               if (child != null) {
                   s.print("caused by: ");
                   child.printstacktrace(s);
                   if (child instanceof applicationexception) {
                       break;
                   }
                   parent = child;
               }
           }
       }
       public void printstacktrace(printwriter w) {
           // print the stack trace for this exception.
           super.printstacktrace(w);
           throwable parent = this;
           throwable child;
           // print the stack trace for each nested exception.
           while((child = getnestedexception(parent)) != null) {
               if (child != null) {
                   w.print("caused by: ");
                   child.printstacktrace(w);
                   if (child instanceof applicationexception) {
                       break;
                   }
                   parent = child;
               }
           }
       }
       public throwable getcause()  {
           return cause;
       }
}






清单 1 中的代码很简单;我们已经简单地将多个异常“串”在一起,以创建单个、嵌套的异常。但是,真正的好处在于将这种技术作为出发点,以创建特定于应用程序的异常层次结构。异常层次结构将允许 ejb 客户机既接收特定于业务的异常也接收特定于系统的信息,而不需要编写大量额外代码。

异常层次结构
异常层次结构应该从一些十分健壮而又通用的异常入手,如 applicationexception。如果您将顶级异常搞得太具体,那么其结果是您今后将不得不重新构造层次结构,以适应某些较通用的情况。

因此,让我们假定您的应用程序要求 nosuchbookexception、insufficientfundsexception 和 systemunavailableexception。您不必创建这三个异常,让它们继承 applicationexception,然后只需提供极少几个必须的构造器来创建格式化的消息。清单 2 是此类异常层次结构的示例:

清单 2. 异常层次结构 package com.ibm.library;
import com.ibm.applicationexception;
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)很容易(有时也很诱人),但如果一开始就建立一个可靠的、深思熟虑的异常模型,则您将在应用程序上少花很多精力。创建一个嵌套的、层次结构化的异常框架将改进代码的可读性及其可用性。


关于作者
brett mclaughlin 从 logo 时代(还记得那个小三角吗?)就开始从事计算机工作了。他现在专门研究用 javajava 相关技术构建应用程序基础结构。过去几年他一直在 nextel communications 和 allegiance telecom, inc. 致力于实现这些基础结构。brett 是 java apache 项目 turbine 的共同创始人之一,该项目为使用 java servletweb 应用程序开发构建可重用的组件体系结构。他还是 ejboss 项目(一个开放源码 ejb 应用程序服务器)和 cocoon(一个开放源码 xml web 发布引擎)的志愿开发人员之一。可通过 brett@oreilly.com 与 brett 联系。


 


关键字 本文所属关键字

相关 与本文相关文章

分类 所有文章关键字导航

源码编程相关

Java   Asp   PHP   .Net   XML   C/C++   CGI   VB   Jsp   J2ee   J2se   J2me   EJB   Servlet   Tomcat   Resin   Struts   Weblogic   Eclipse   ANT   GUI   JMS   Web servise   IDEA   Webphere   Hibernate   Spring   Jboss   Applet   Swing   Socket   Javamail   Perl   Ajax   P2P   安全   模式   框架   测试   开源   游戏

SQL数据库相关

My-SQL   Ms-SQL   Access   DB2   Oracle   Sybase   SQLserver   索引   存储过程   加密   数据库   分页   视图  

手机无线相关

3G   Wap   CDMA   GRPS   GSM   IVR   彩信   短信   无线   增值业务

网页设计制作相关

HTML   CSS   网页配色   网页特效   Javascript   VBscript   Dreamweaver   Frontpage   JS   Web   网站设计

网站建设推广相关

建站经验   网站优化   网站排名   推广   Alexa

操作系统/服务器相关

Windows XP   Windows 2000   Windows 2003   Windows Me   Windows 9.x   Linux   UNIX   注册表   操作系统   服务器   应用服务器

图形图像多媒体相关

Photoshop   Fireworks   Flash   Coreldraw   Illustrator   Freehand   Photoimpact   多媒体   图形图像

标准 网站致力的规范

Valid CSS!

无不良内容,无不良广告,无恶意代码

Valid XHTML 1.0 Transitional

creativecommons