最近刚好作了一个产品的异常处理规范,把我做的也拿出来晒晒,和大家讨论一下。
1、CheckException or UnCheckedException
个人倾向用UnCheckedException。我见过的最多的处理异常的代码就是记录日志或转换后抛出,好像做其他操作的少之又少。我以前还见过有人不管三七二十一,抓到什么抛什么,结果一个接口抛出了3-5种CheckException。别扭啊,呵呵。
当然,最大的缺陷就是对接口调用者的使用。至少UnCheckedException可以让接口调用者选择catch还是不catch。
因为这是一个遗留系统,都使用了CheckedException,不过好在使用的比较规范,没有太大麻烦。
2、异常信息
因为开发者众多,异常信息五花八门,有中文的有英文的,有简单的,有复杂的。散落在各个类里,修改起来很麻烦。
我的处理办法是,定义异常码,异常信息和异常码一一对应,定义在properties文件里,抛出异常的地方只引用异常码。这样,如果需要修改异常信息,只要修改properties文件重新打包即可。异常码分为原因码和位置码。位置码一般用在业务逻辑类里。比如一般用到的Facad模式,可能一个接口定义了n个方法,每个方法都抛出XXXBusinessException(XXX用于区分业务子包),抛出的异常码在代码里顺序定义即可。原因码表示某类异常,比如SQLException引起的异常都为8000。
异常码中原因码按包、类、接口定义,比如:前两位表示一级子包,接下来两位留给二级子包,接下来两位表示类,最后3位表示方法。
异常信息如果需要,可以保留现场信息。比如:“用户[abc]已存在”。这样的信息就比“用户已存在”明确。这个功能可以通过java.text.MessageFormat实现。
3、异常的链式抛出
即,在调用底层接口时捕获到异常,需要转成其他的异常抛出时,使用带Throwable的构造器。这样可以保留最原始的出错信息。
异常基类代码:
- public class BaseException extends Exception {
- protected long errorCode;
- protected String[] args;
- /**
- * @param errorCode
- */
- public BaseException(long errorCode) {
- super();
- this.errorCode = errorCode;
- }
- /**
- * @param errorCode
- * @param cause
- */
- public BaseException(long errorCode,Throwable cause) {
- super(cause);
- this.errorCode = errorCode;
- }
- /**
- * @param errorCode
- * @param cause
- * @param args @see {@link java.text.MessageFormat#format(Object)}
- */
- public BaseException(long errorCode,Throwable cause,String[] args) {
- super(cause);
- this.errorCode = errorCode;
- this.args = args;
- }
- /* (non-Javadoc)
- * @see java.lang.Throwable#getMessage()
- */
- public String getMessage() {
- String message = "";
- if (this.args == null || this.args.length == 0) {
- message = MessageSource.getInstance().resolveCodeWithoutArguments(
- this.errorCode);
- } else {
- message = MessageSource.getInstance().resolveCode(this.errorCode)
- .format(this.args);
- }
- return this.errorCode+":"+message;
- }
- /**
- * @return the errorCode
- */
- public long getErrorCode() {
- return errorCode;
- }
- /**
- * @return the args
- */
- public String[] getArgs() {
- return args;
- }
- }
其中:MessageSource这个类就是根据是否有现场信息,分别处理,得到完整异常信息表述字符串。
关于主贴中嵌套打印的部分,从JDK1.4开始,Throwable就是嵌套打印的,所以不必再覆盖那几个方法了。
现在假使,有AException 和BException,都从BaseException继承。在方法里,我们捕获了AException,需要转换成BException抛出,这时候我们就非常希望能直接用AException的信息(errorCode和args)。所以,如果再来这样一个构造器似乎更好:
- public BaseException(BaseException cause){
- this.errorCode = cause.getErrorCode();
- this.args = cause.getArgs();
- }
但是,因为Exception有一个构造器:public Exception(Throwable cause),所以这样的构造器就是重载(overload),而不是覆盖(Override)。重载,入参又是父子类关系,这样的方法很容易混淆,所以就没有提供。
我按照这个异常处理规范,花了两三天时间把代码重构了一遍。似乎用起来挺好使