JavaWeb应用中错误和异常处理方法研究

0.引言      

       大多数的JavaWeb应用一般都采用多层架构[, 即软件结构采用分层的思想。这种分层架构的软件必然使软件的耦合性降低,内聚性加强,但分层带来的缺点是增加了软件开发的重复性工作,同时分层也会使应用变得相对复杂,进而可能会使编程产生一些不必要的错误。由于JavaWeb应用业务逻辑的复杂性,容易发生一些意想不到的错误和异常,给系统的调试带来不必要的麻烦,不友好的提示信息使编程者对错误和异常无从下手。特别是当发生异常时,Java异常栈输出的信息只能给程序员来看,是绝对不能展示给用户的。而且一旦项目发布成最终版本运行时,也十分有必要跟踪和记录所发生的错误和异常的详细信息,并返回一个特定的友好界面给用户。 在传统的JavaWeb应用中,应用程序通常是采用硬编码的方式来避免将要发生的错误和异常,对应用程序中的异常经常使用try…catch语句进行处理,哪 里可能会抛出异常,就在哪里进行捕获,这些语句似乎成了应对编译器而不得已的手段。为了简化,往往在catch代码块中什么也不写,try…catch语句成了一种摆设。甚至干脆就用关键字throws声明抛出异常,不对异常进行处理。这样不但会使写出来的程序杂乱无序,到处都是一些无用的try…catch语句,程序的可读性、可修改性大大降低,而且还会增加程序员的工作量。

      针对以上问题,考虑到JavaWeb应用开发、日后运行、升级和维护,在Java异常机制的基础上构建了一个在JavaWeb环境中的错误处理和异常处理的框架模型。该模型对异常和错误进行统一管理,并在一个集中的位置统一处理,程序的可读性、可维护性、可修改性、鲁棒性等都得到了提高。本文使用Struts、Spring、Hibernate架构三层JavaWeb应用。Struts作为表示层,Spring作为业务逻辑层,Hibernate作为持久层。


1.Struts+Spring+Hibernate三层架构

       一个好的JavaWeb项目的软件体系结构应该分多层的,分的层越多,程序中模块之间的耦合度就越低,模块的内聚性就越强,但应用程序也会变得相对复杂。一般情况下,分成三层比较适中。本文讨论的不是软件体系结构,而是在Java异常机制的基础上构建一个在JavaWeb环境中的错误处理和异常处理的通用框架模型。所以在此仅简单地给出了软件的三层体系结构模型,即JavaWeb三层结构模型,如图1所示。

JavaWeb应用中错误和异常处理方法研究_第1张图片


2.错误与异常处理模型


       本文所说的错误是特定的一类错误,例如保存记录时该记录已经存在;删除时该记录不存在,连接数据库出错,事务没有及时打开或关闭等都将其视为错误。而异常在Java中都是Throwable类的子类,在它之下包含两个子类Error与Exception,当在Java虚拟机中发生动态连接失败或其他的定位失败时,Java虚拟机抛出一个Error对象。当程序不捕获或抛出Errors对象时,永远不会遇到需要实例化Error的应用,那么需要关心的就是Exception类。


2.1错误和异常处理原则

      本文对错误的处理方式是采用抛出自定义类型的异常,这样便于对异常和错误进行统一管理,提高JavaWeb应用程序的健壮性。JavaWeb应用开发中产生的异常都应该继承Exception(属于checkedexcpetion类型)。而且JavaWeb应用一般采用三层或多层架构,程序员没有必要在各个层中对错误和异常进行处理,应用中的每一层在包装并传递异常时要过滤掉Runtime-Exception,从责任这个角度看uncheckedexception是程序应该负担的责任;checkedexception是具体应用负担的责任。无论如何我们都不应该将uncheckedexception这样的异常暴露给客户的,因为他们没有解决这个问题的责任,应该将这种异常封装成checkedexception类型的异常,由具体的应用程序来负担这个责任。


2.2错误处理策略

       程序中可能会发生很多的错误,例如当执行删除记录、插入记录、修改记录和复杂的业务逻辑等错误,当出现了错误应该如何处理呢?
      传统的处理方法是采用编程的方式来提高应用程序的健壮性。当发生错误时,由程序来控制给用户提示友好信息或者显示一个错误提示界面。很显然这种处理方式的实质就是增加程序的代码量来弥补程序中的不足,治标没有治本,不能从根本上解决问题。
本文采用的错误处理策略是当发生错误时,将错误和发生错误时转向的页面封装成一个异常对象将其抛出,然后将异常集中到一个统一的位置进行处理。显而易见,采用这种错误处理的方式的优点在于:当运行中的程序发生错误时就抛出一个详细的异常对象,根据发生的异常信息来决定转向到不同的页。避免因采用编程而被忽略的一些错误(由于代码量的增加而导致的错误)。

2.3异常处理策略

       程序中可能会发生很多的异常,例如业务逻辑、未找到指定的文件、类型转换失败等异常时,Web应用程序应该将异常和发生异常时转向的页面封装成一个新的异常对象将其抛出,然后将异常集中到一个统一的位置进行处理。显然,采用这种异常处理的方式的优点在于Java中的异常栈信息没有展现给用户,而是将异常信息和友好的页面展现给用户。
       使用异常对应用程序错误和异常进行统一管理的好处在于:由于Java的异常机制允许调用者可以不对异常进行处理,而用关键字throws抛出异常,这将会使异常向上一级传递,即当前环境没有足够的信息和能力来解决这个异常时,就可以把这个异常交到一个更高级的有能力处理的环境中,在这里将做出对异常的处理,形成一个异常的传递链。这样可以将所有的异常通过这样的传递链集中到一个统一的位置进行统一处理。
      它能使错误代码变得更有条理,与采用硬编码的方式来处理错误方式相比,代码量会明显减少,并且通过这种错误报告机制,只需在一个地方处理错误,即在所谓的“异常处理程序”中处理,这种方式节省了代码,而且把“描述做什么事”的代码和“出了问题怎么办”的代码相分离。总之,与采用硬编码的方式来处理错误的方法相比,异常机制使代码的阅读、编写和调试工作更加井井有条。

2.4异常抛出策略和捕获位置

       在图1所示的JavaWeb三层架构模型中,我们可以利用Java的多态机制,只捕获自定义的基类异常(例如BasicException),它包含了所有应用程序异常的默认行为,但是具体业务逻辑抛出的异常可以是BasicEx-ception类的任何子类异常,使用多态来隐藏异常的具体的实现类。这意味着BasicException异常(仅仅是这个异常)可以放到抛出checked异常的每个方法的throws子句中,不能包含其他任何应用程序异常。当应用程序发生了某个具体的异常(BasicException的派生类)时,应用程序应该做出在哪一界面上显示哪条错误消息的决策,即在什么位置捕获异常。本文选择的位置是控制器(Struts的Action),它恰恰有根据不同的异常来设置不同的错误信息和跳转到不同的错误页面的能力,而且Action的位置是最接近客户端表示层的,所以这个位置是最恰当的。

       这样所有的异常会在一个集中的公共位置得到处理,使用模板方法(TemplateMethod)设计模式并结合Struts的DispatchAction编写一个模板方法,并在该模板方法中捕获BasicException异常,这将会捕获到所有的子类异常。因此我们采用的策略是:持久层中的所有方法都抛出BasicException异常,不对其处理;业务逻辑层中的所有方法采用像持久层中的策略,同样不对异常进行处理,即抛出BasicEx-ception异常。利用这种传播异常的通用机制,将异常以一种普适的方式集中到距离客户端最近的控制器中处理。这样处理有很多优点:不需要在throws子句中放入大量的checked异常;throws子句中只需要有一个异常,不需要再对应用程序异常使用混乱的catch块;如果需要处理它们,一个catch块(用于BasicException)就足够了,程序员也不需要亲自进行异常处理(日志记录以及获取错误代码),使编程者更加专注于业务逻辑的处理,而错误和异常的处理与记录可以使用后文提到的Facade接口完成。

3.错误与异常处理模型实现

3.1错误与异常层次结构

       本文仅以两类错误和两类异常为例展开讨论,但本文构建的错误和异常处理框架模型是适合任意多的错误和异常种类,只要它们间接继承BasicException类,例如数据没有找到类型的错误,例如DataNot-FoundExceptionextendsBasicException。逻辑异常:LogicaExceptionextendsBasicException数据查找错误:DataExceptionextendsBasicExcep-tion权限异常:RightExceptionextendsBasicException登录错误:LoginExceptionextendsBasicException在具体工程项目实现时,可以根据需要增加错误和异常的种类。

3.2应用与模型交互

       本框架模型是在StrutsAction层负责决定对错误与异常采取什么操作。这种决策涉及到识别抛出异常的代码。此外还需要知道在处理错误与异常之后应该把错误消息重定向到哪一界面。我们需要对基于异常类型获得错误代码这个过程进行抽象,同时还应该执行日志记录。我们把这些工作抽象成一个接口,该接口基于外观设计模式也叫总管模式(Facade模式)[10~11],该模式为子系统中的一组接口提供一个统一外部访问入口。
       外观定义了一个更高级别的接口,使子系统变得更加易于访问,是用于处理所有派生自BasicException的异常的整个异常处理系统的外观。也就是说该接口是连接应用程序与框架模型的一个桥梁,为复杂的异常处理框架模型提供一个统一的操作门面。采用这种设计模式的优点在于:
       它为应用逻辑屏蔽了异常框架模型子系统的复杂性,使得异常框架模型系统更加容易使用。实现了子系统与应用逻辑之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的。方便在子系统中添加新功能,只需要在Facade里添加新的方法,然后调用拥有新功能的类或方法就可以了,原来实际执行任务的类不需改变。如图2所示,给出了异常处理模型的时序图。

       从图2可以看出采用这种Facade模式使得业务逻辑控制器只与异常处理门面打交道,简化了业务逻辑关于异常处理的编程,使程序员更加专注于业务逻辑的编程。如图3所示,给出了异常处理模型的时序图。

 JavaWeb应用中错误和异常处理方法研究_第2张图片                                     


JavaWeb应用中错误和异常处理方法研究_第3张图片                                                        



       从图3可以看出异常处理系统通过异常处理门面,调用异常工具类将发生的所有可能的错误和异常统一封装成ExDTO对象返回给控制器,同时也将异常的详细信息记录到日志文件中有助于日后的调试以及查找错误。
      下面给出一个在StrutsAction方法中进行异常处理的例子。

try{
/*
处理应用程序的业务逻辑
调用业务逻辑层的业务逻辑方法,该业务方法声明抛出Ba-
sicException
*/}
catch(BasicExceptione){
//定义错误和异常处理者
ExceptionHandlereh=newExceptionHandler();ExDTOexDto=eh.handleException(“context”,user,ex);ActionMessagesmessages=newActionMessages();messages.add(ActionMessages.GLOBALMESSAGE,newActionMessage(exDTO.getMessageCode()));
saveMessages(request,messages);//转到用户有好界面
returnmapping.findForward("errorAndExceptionOccur");}

3.3精简Struts的Action的代码

      从上面编写Action的方法可以看出,每个方法都必须书写捕获异常的模板代码,应该避免这样的编码方式,解决方式是利用Struts的DispatchAction的工作机制(这里不对该类的工作方式进行介绍,可以查看帮助文档),并结合模板方法模式(TemplateMethod)[10,11]重写模板方法execute,并将不变的处理错误和异常模板的代码写在execute方法中,把具体的可变的业务逻辑控制方法留给子类来实现。下面给出一个自定义的
StrutsDispatchAction中的execute方法的具体实现例子。
try{
ActionForwardfoward=
dispatchMethod(mapping,form,request,response,name);returnfoward;}
//处理用户定义错误和异常catch(BaseAppExceptione){
//定义错误和异常处理者
ExceptionHandlereh=newExceptionHandler();ExDTOexDto=eh.handleException(expDTO.getContext(),userId,e);
ActionMessagesmessages=newActionMessages();messages.add(ActionMessages.GLOBAL_MESSAGE,newActionMessage(exDTO.getMessageCode()));saveMessages(request,messages);//转到用户有好界面
returnmapping.findForward("errorAndExceptionOccur");}
//处理非用户定义错误和异常catch(Exceptionex){
//对错误和异常进行记录
ExceptionUtil.logException(this.getClass(),ex,userId);throwex;
}finally
{exDisplay.set(null);}

经过这样的处理,每个Structs的Action只要继承DispatchAction类就可以自动继承错误和异常处理代码,节省大量代码的编写。

4.结语

       本文提出了一种关于错误处理和异常处理的框架模型,并利用三层架构思想实现了该模型,解决了JavaWeb应用中错误处理和异常处理普遍存在的问题。当应用发生错误和异常时,该模型能将错误和异常详细信息记录到日志文件中,同时控制器能够根据该信息将页面跳转到指定的网页上。
       由于本文重点介绍错误与异常处理模型,故可以选用不同的工具加以实现。例如表示层可以选用JSF、Struts2;业务逻辑层可以选用Jdon;持久层可以选用其他的ORM框架,例如KylinORM、MyBatis等。

你可能感兴趣的:(设计模式,java,web,异常处理)