当前位置:编程学习 > JSP >>

继续讨论Exception的处理

答案:
继续讨论Exception的处理




 


简述




 


用Java在开发系统的时候,Exception的处理往往是比较复杂的。如何处理开发中遇到的Exception,如何将合理的异常信息呈现给客户是开发人员必须要考虑的问题。




 


关于Exception的处理的文章在很多地方都可以看到,本文除了做一个总结之外,还将结合Design by Contract,JDK 1.4引入的assertion,以及如何用Spring的AOP处理Exception做进一步的探讨。




 


Exception的分类




 


从JDK的API中我们可以看到,Java把异常分为了Error和Exception两大类,在Exception中又分为checked exception和runtime exception。从系统开发的角度上,我们可以把exception分为:




 


·         JVM异常。这种异常我们不应该捕捉,因为它的出现意味着一些比较严重的错误,比如OutOfMemoryError,StackOverflowError等;



·         系统异常。大多数情况下,系统异常以RuntimeException的形式出现,比如NullPointerException, ArrayOutOfBoundsException等,这时往往意味着我们的程序里面出现了Bug;还有一种情况,例如我们没有办法通过JNDI找到某个资源,也应该属于系统异常。系统异常的主要特点是,当我们遇到这种异常的时候,我们没有合适的办法处理,或者说我们不能已一个合理的方式告诉最终用户系统出现了什么错误。很难想象用户看到一个NPE,并在界面上看到一堆stack trace是什么感觉。这种异常应该在单元测试以及集成测试的时候被检测到,在发布的时候应该尽可能不出现这样的问题;



·         应用异常。这种异常是由我们的系统。这些异常的出现对用户来说,可能是因为某个验证没有通过,某个操作的步骤出现错误等,比如插入数据库的时候出现主键重复的情况等。总之,这些异常的信息可以通过一个用户看的懂的方式显示给用户。




 


Design By Contract(DBC)




 


我们暂时把Exception的处理放在一边,先看看Design by Contract的概念。




 


对于任何一个软件系统来说,一个重要的目标就是可靠性,即正确性和健壮性。系统的正确性主要看这个系统是不是符合Specificatoin,健壮性主要是指当遇到Specification没有涉及的情况,即异常情况的时候能不能以一种合理的方式解决。




 


DBC的主要思想是一个类和它的客户程序之间有一个合同;客户程序必须保证调用这个类之前某些前提条件必须满足(Precondition),而这个类必须保证在被调用之后的某些属性和状态是正确的(Postcondidtion/Class Invariants)。如果能有一种方式能够让编译器检查这些Precondition和Postcondition是否正确,这个合同是否被满足,那么出现的错误可以被立即捕获。




 


例如,一个类需要一个setMonth( int month )的方法,我们一般的实现方法大致如下:




 


public void setMonth( int month )



{



       if( month > 0 && month < 13 )



       {



              throw new IllegalArgumentException( “” );



       }



       this.month = month;



}




 


但是按照DBC的概念,应该由客户代码,而不是setMonth方法保证传进来的参数是一个正确的数值,而setMonth应该保证当方法执行之后,month的值被正确设置,同时保证该类处在一个正确的状态。所以setMonth中对于参数month的验证就不在这里出现了。从这个例子中可以看到,DBC的引入对不同类的责任有一个明确的划分,原来的代码里面setMonth的责任现在被转移到了客户代码里面。




 


目前,编程语言中对DBC支持较好的是Eiffel,而在JDK1.4中引入的assertion则为Java在DBC方面提供了一些支持。




 


Java Assertion




 


Java虽然不直接支持DBC,但我们可以利用JDK1.4提供的assertion功能做一些这方面的工作。下面简单介绍assertion的用法,详细的信息请参见sun网站上的资料(http://java.sun.com/j2se/1.4.2/docs/guide/lang/assert.html)。




 


Assert有两种用法



·         assert BooleanExpression和



·         assert BooleanExpression : DetailMessage




 


系统运行的时候如果检查到BooleanExpression是false,那么就是抛出一个AssertionError。DetailMessage如果提供,会通过AssertionError的构造函数传进去。




 


由于assertion是在JDK1.4引入的,为了编译包含有assertion应用的Java程序,我们需要在javac中打开开关source=”1.4”,如在ant的build文件中,我们一般这么写:




 


<javac srcdir=”${src.dir}” destdir=”${build.dir}” source=”1.4” target=”1.4”>



       <classpath refid=”class.path”/>



</javac>




 


而在IDE环境中注意做相应的变化。




 


在缺省情况下,assertion在运行时是被禁用的,我们通过开关-ea和-da来打开和关闭assertion的应用,如:




 


java –ea SomeClass




 


有了assertion的帮助,我们在上面的assertion代码中做如下的改变(注意:请参见Sun的assertion文档,这里仅仅示例assertion的用法,并没有考虑assertion的best practice中的某些原则):




 


public void setMonth( int month )



{



       assert month > 0 && month < 13;



       this.month = month;



       //assert the state of this class in valid.



}




 


在开发之前,我们和客户代码先签订合同,我们这里在调用方法之前先检查precondition是不是已经满足,如果没有满足,我们就直接抛出AssertionError,因为我们合同里面已经做了约定,输入参数的正确性应该由客户保证(第一个assertion不应该出错),而我们这里应该保证方法被调用之后的postcondition和class invariants的正确性。




 


引入了assertion,我们一般在开发过程中应该打开-ea开关,这样可以在单元测试的时候能够捕捉到相当数量的自己的bug,从而保证系统的健壮。




 


Java Assertion Best Practice




 


Sun的assertion使用文档上对什么时候使用assertion做了指导,对其中的一点,我们存在一定的疑问,认为Sun的提法稍显武断(谁这么胆大包天,竟敢质问Sun的伟大J).




 


[Do not use assertions to check the parameters of a public method.]




 


在我们实际开发的过程中,我们认为,Sun这里提到的public method应该理解为不同模块之间的接口,而不是单纯意义上的Java类的公用方法。假设一个小组提供一个底层支持模块给其他小组使用,而这个小组内部也分了很多层次,那么这里的Public method我们认为应该指提供给其他模块/组使用的API,而内部的某些方法,虽然是公用的,仍然可以使用assertion,因为这个合同属于是内部合同。所以我们认为这里的public method应该指存在合同关系的两个模块之间接口的public method。




 


另外,assertion的使用在概念会和数据验证出现一定的重叠,这个我们需要根据实际的情况决定什么时候使用assertion。




 


Exception的处理




 


回到正题,根据上面的讨论,对于三种Exception来说,JVM的Exception我们在系统实现上不予考虑,对于一部分系统异常来说,我们可以使用assertion解决掉一部分由于系统的bug引起的异常,对于其余的Exception要正确处理,而处理的标准则是一方面能够给最终用户提供有意义的出错信息,另外一方面,由于尽管我们做了认真的测试,我们仍不能保证系统在发布之后不会出现任何问题,因此需要考虑能够有合理的方法准确定位错误,为开发人员纠错提供依据。




 


在实际的应用中,关于Exception类的设计我们可以借鉴一下Spring中关于对Exception的设计。通过考察Spring DAO支持中Exception的

上一个:在Tomcat里发布程序
下一个:IntelliJ IDEA培训

CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,