某大型银行深化系统之二十:异常规范

传送门 ☞ 轮子的专栏 ☞ 转载请注明 ☞ http://blog.csdn.net/leverage_1229

1异常抛出与捕捉规则

1.1任何抛出异常的方法必须先声明异常

{
    // Constructor
    public MyClass( String name ) throws NullPointerException, llegalArgumentException {
        ...
    }
}

1.2异常声明后,调用异常对象的构造器抛出异常

public MyClass( String name ) throws NullPointerException, IllegalArgumentException {
    if ( name == null )
        throw new NullPointerException( );
    if ( name.length == 0 )
        throw new IllegalArgumentException( );
    ...
}
        Java异常都从Throwable类继承而来。

1.3用try,catch声明捕捉异常

try {
    MyClass newobj = new MyClass( name );
} catch ( NullPointerException e ) {
	//  Do something about it
} catch ( IllegalArgumentException e ) {
	//  Do something about it
}
        当try中的语句产生异常时,在异常处理函数捕捉了该异常而又没有重新抛出异常时,则在执行完处理函数后,将跳出发生异常的try块,接着执行下面的语句。当try中的语句产生异常时,在异常处理函数捕捉了该异常而又重新抛出异常时,则将把异常抛出到上一层,发生异常的语句之后的代码将不执行。如果没处理相应异常的处理函数,将把异常抛出到上一层,发生异常的语句之后的代码将不执行。如果想捕捉所有异常,只要捕捉Exception异常就行。如果某段代码不管是否发生异常都要执行,那可把它放入finally块中,以finally进行清理。

2取得异常信息的几个常用方法

String getMessage()、getLocalizedMessage 、toString()取得异常对象中的信息。
void printStackTrace()、Throwable fillStackTrace()。printStackTrace打印出Throwable和其call stack trace;FillStackTrace则在调用点设立新的stack trace信息。
RuntimeException异常。RuntimeException及其子类所代表的异常我们在程序中不用进行捕捉,如果发生此类异常,Java会自动抛出相应的异常对象。

3继承中异常规则

3.1构造函数中的异常规则

        某个derived class构造函数的“异常规格接口”可以比其所调用的父类的构造函数的异常规格接口宽,但决不能变窄。
        derived class的构造函数必须在自己的异常规格中声明所有base class构造函数的异常规格中所声明的异常。
        在derived class的构造函数的异常规格中还可以声明新的异常,即声明在base class构造函数的异常规格中没有声明的异常。

3.2非构造函数的异常规则

        某个函数的“异常规格接口”在继承和重载中可以变窄,但决不能变宽。要覆写base class的函数时,如果被覆写函数(base class中的函数)的异常规格中声明了异常,那么覆写函数(derived class中覆写了base class中的函数的那个函数)的异常规格中可以声明:
与被覆写函数完全相同的异常;
被覆写函数异常规格中的部分异常或其子类异常;
不声明异常规格。

3.3.产生对象的异常规则

        在产生一个对象时,捕捉的是产生对象时所调用的构造函数中所声明的异常。

3.4.函数调用时的异常规则

        当把一个对象向上转型为它的base class时,并通过转型后的reference进行函数调用时,我们要捕捉的是其base class的异常声明。

        当用对象的原始类型来调用函数时,只需捕捉所调用的覆写函数的异常。

4异常分类

4.1.Checked Exception

        Checked Exception是指那些需要程序显式的处理(捕获或者继续抛出)的异常。在项目中Checked Exception主要可以分为以下三种:

4.1.1BizException

        项目自己定义的业务异常,BizException下继承了若干个子异常。也就是说BizException是所有项目自定义异常的根类。

4.1.2RollbackableBizException

        项目自己定义的业务异常,是BizException的子类。

4.1.3Other Checked Exception

        其他非本项目定义的Checked Exception类异常

4.2.Unchecked Exception

        Unchecked Exception是指那些不需要程序进行显式处理(捕获或继续抛出)的异常,如果程序需要处理这类异常则其处理方式和Checked Exception一样。在项目中Unchecked Exception 主要可以分为以下两类:

4.2.1DataAccessException

        这类异常主要是Spring框架定义的数据访问类异常,其具体类型根据数据库的不同和数据库所产生的错误的不同而不同。该类异常描述了数据库访问过程中所产生的错误。其下还有若干个子类异常,下面列出了这些异常:
CleanupFailureDataAccessException:数据访问操作之后无法正常执行清除工作,比如JDBC访问之后无法正常关闭Connection。
ConcurrencyFailureException:并发时可能出现此类异常,其下有若干子类异常来标识乐观锁和获取锁失败这两类异常信息。
OptimisticLockingFailureException:在违反了乐观锁机制的情况下会抛此异常
PessimisticLockingFailureException:违反悲观锁机制的情况下会抛出此异常。此异常是由SQLException转换过来的。
CannotAcquireLockException:无法获取锁异常,一般在数据更新时获取锁失败时抛出
DeadlockLoserDataAccessException:当一个并发进程进入死锁链,通常需要抛出异常并回滚该进程产生的事务 。
DataAccessResourceFailureException:当访问某个资源失败时抛出此类异常,比如无法用JDBC与数据库之间建立连接。其下有如下几个子类
CannotCreateRecordException:由于连接器(Connector)内部原因造成创建CC I(Common Client Interface)记录失败时抛出此异常。
CannotGetCciConnectionException:严重异常,无法通过CCI(Common Client Interface)连接EIS(企业信息系统)抛出此异常。
CannotGetJdbcConnectionException:严重异常,无法通过JDBC连接到关系数据库(RDBMS)。
DataIntegrityViolationException:当向数据库插入一条数据或修改数据库中的数据时违反了数据库的完整性约束时抛出此异常。
DataRetrievalFailureException:当无法获取预想的数据时抛出此异常。其下有若干子异常。
IncorrectResultSizeDataAccessException:当获取的数据数量不是预想的数量的时候抛出此异常,比如预想获取到1条数据,但是获得了0条或者多条数据时抛出此异常
LobRetrievalFailureException:LOB无法被获取的时候抛出此类异常
ObjectRetrievalFailureException:通过映射对象(Object)的标识符来获取映射对象失败。
HibernateObjectRetrievalFailureException:将Hibernate和 UnresolvableObjectException, ObjectNotFoundException, ObjectDeletedException和WrongClassException转换为此异常。
InvalidDataAccessApiUsageException:当错误的使用了API的时候抛出此异常
InvalidDataAccessResourceUsageException:当恰当的访问数据时出现此异常,比如,SQL语法不正确。其下有多个子异常类。
BadSqlGrammarException:定义的SQL语句是非法的,有语法错误的时候抛出此异常,一般情况下都是由一个SQLException引起的。
CciOperationNotSupportedException:当连接器(Connector)不支持特定的CCI(Common Client Interface)操作的时候抛出此异常
HibernateQueryException:当HQL(Hibernate的数据库查询语句)语句出现错误时抛出此异常。
IncorrectUpdateSemanticsDataAccessException:当更新数据库数据时出现非预想的结果,这个时候数据库的事务还没有回滚。比如预想修改1条数据,但实际修改了3条数据。
InvalidResultSetAccessException:访问结果集不合法,比如非法的字段名等等。
RecordTypeNotSupportedException:当由于连接器(Connector)不支持预想的CCI记录(record)类型而造成创建一个CCI 记录(record)的失败的时候抛出此异常。
TypeMismatchDataAccessException:当Java类型与数据库(RDBMS)的类型不匹配的时候出现此异常。
UncategorizedDataAccessException:无法分类的异常,其下有若干子异常
HibernateJdbcException:此异常是对Hibernate的JDBC异常的简单包装。
HibernateSystemException:当发生其他一些Hibernate错误是否抛出此异常,这些错误与org.springframework.dao中所定义的异常无法匹配
SQLWarningException、UncategorizedSQLException:无法分类的SQLException

4.2.2Other RuntimeException

        其他类型的RuntimeException,例如:NullPointException等。

5异常使用原则

        使用异常主要是为了报告系统运行中出现的非正常情况。目前异常使用的方法有两种。一种是使用不同的异常类来表示不同的异常,这些异常类的类名就表达了系统所处的非正常状态。另一种是系统中出现的所有异常都用很少的几个异常类来表示,通过这些异常类中Message来表达具体的异常信息。项目主要使用的的是后一种方法。但在某些主要的子系统或与外系统交互时也会采用一些第一种方法。
        Message主要由两部分组成Key和Param[],其中key的命名规则为:级别(1位)_模块名(可选)_逻辑名,其中级别主要分为Warn(W)和Error(E)两个级别。模块名:指应用功能的名称。逻辑名:从某种角度上来说可以理解为异常的编码。Param[]是具体的异常信息的参数。其具体使用方法如下,首先应用程序从异常中获取Message,然后从Message中获取key值,根据key值从resource文件中获得异常描述信息,将Param[]通过填空形式放入获得异常描述信息中形成一个完整的异常描述信息。

5.1Checked Exception

        根据是否需要事务作RollBack操作而使用不同类型的异常。

5.1.1RollbackableBizException

        需要事务RollBack则抛出RollbackableBizException。

5.1.2BizException

        如果不需要事务作RollBack操作则抛出BizException。

5.1.3Other Checked Exception

        在项目的应用程序中只负责处理这类异常,不产生或使用这类异常。

5.2Unchecked Exception

        鼓励应用程序重用一些通用的UncheckedException异常,如IllegalArgumentsException, NullPointException等。

5.2.1DataAccessException

        大多数情况下这类异常应用程序不用关心,特别是DAO类的对象不用关心这类异常。它是Spring框架所抛出的数据库访问的异常。

5.2.2Other RuntimeException

6异常处理原则

        异常处理需要遵循几点基础原则:
第一,能处理则进行处理和关心的异常,不能处理或不关心的异常则继续抛出。决不允许出现捕获异常但又不做任何处理或抛出操作,这样会丢失异常信息。
第二,异常抛出源需要在抛出异常之前将异常写入日志中。以备进行检查。
应用程序所处层次的不同其对异常的处理也有所差别。下面列出了每个层次的应用程序在处理异常的一些原则。
        下图是项目中异常使用的一个图释:


6.1web layer(展示层)

        该层将从Business layer获取的异常根据异常所传递的消息的不同来区分当前异常是否可恢复。这里指的可恢复是指当出现异常后操作人员可以通过改变输入来解决出现的异常。按照可恢复性的不同,该层应用对异常的处理也有所区分。具体操作过程中该层根据获取异常Message来确定处理的方式。如果Message的级别是Warn则需要将异常信息显示到操作员录入页面并保留上次录入信息,如果Message级别是Error则需要将信息转向专门的异常显示页面显示异常信息。

6.1.1可恢复异常

这类异常处理的时候应将异常信息显示在用户输入的页面并保留相关的输入信息,以便让操作员通过修改输入来纠正异常。

6.1.2不可恢复异常

        该类异常由于无法通过简单的改变操作员的输入来进行纠正,因此这类异常也比较严重。需要将该类异常的信息转发到专门的异常信息显示页面上。

6.2Business layer(业务层)

        该层可以分为UCC(用例控制)层和Service(服务)层。对于该层的异常处理也应该分成两部分来看待。

6.2.1UCC Layer(UCC层)

        该层除了负责处理本层所产生的异常外还要负责Service层所抛的所有异常。对于其根据获取的异常的类型来做相应的处理。一般情况下对于从Service层获取的异常都是再次抛出,但在抛出前需要控制事务是否需要RollBack。如果获取的错误是RollbackableException和UnChecked Exception则需要事务RollBack操作,如果是BizException则需要根据具体业务用例来确定是否需要事务RollBack,但原则是在抛出异常之前如果已经发生了数据库的Create、Update、delete等修改类型的操作则需要进行RollBack操作,否则则不需要RollBack。UCC层可以处理除了Other Checked Exception以外的所有异常类型。

6.2.2Service Layer(服务层)

        Service层来除了负责处理自身处理过程中所产生的异常还要负责Integration Layer所抛出的异常的处理。一般情况下不需要处理Unchecked Exception,但如果业务需要也可以捕获Unchecked Exception并作相应的处理。Service所能够处理的异常类型是本文所提到的所有类型的异常。

6.3Integration layer(整合层)

        整合层根据数据源类型的不同异常的处理原则也不同。

6.3.1数据库

        当数据源为数据库的时候,应用程序不需要捕捉数据库的异常或者Hibernate产生的异常(这些工作都有Spring框架完成)。在该层处理过程中如果出现异常则此类异常的严重等级都比较高,都需要做事务的RollBack操作。因此在这里出现异常都使用DataAccessException异常,当然具体异常类型可以为DataAccessException下的一个字类型。

6.3.2其他外部系统

        数据源为其他外部系统的时候,该层对外部系统所产生的异常进行相应的封装。封装建议按照用异常类型来区分是什么异常的形式进行。这里抛出的异常一般都是特定的异常不是BizException类型的。Service在接收到该层抛出的异常以后根据异常的类型装换成相应Message放入BizException中重新抛出或者自己消化,但根据需要的不同也可以将异常直接重新抛出。

posted @ 2013-05-27 21:40  Innosight  阅读(588)  评论(0编辑  收藏  举报