n层结构的J2EE项目中的异常处理之三 转载

3.设计一个新的异常类

   在设计一个新的异常类时,首先看看是否真正的需要这个异常类。一般情况下尽量不要去设计新的异常类,而是尽量使用java中已经存在的异常类。

如java 代码IllegalArgumentException, UnsupportedOperationException
                       
不管是新的异常是chekced异常还是unChecked异常。我们都必须考虑异常的嵌套问题。
java 代码
public void methodA()throws ExceptionA{   
         …..          
          throw new ExceptionA();        
}   
方法methodA声明会抛出ExceptionA.

public void methodB() throws ExceptionB

   methodB声明会抛出ExceptionB,当在methodB方法中调用methodA时,如何将ExceptionA继续往上抛出。
一个办法是把methodB声明会抛出ExceptionA, 但这样已经改变了MethodB的方法签名。一旦改变,则所有调用methodB的方法都要进行改变。

另一个办法是把ExceptionA封装成ExceptionB,然后再抛出。如果我们不把ExceptionA封装在ExceptionB中,就丢失了根异常信息,使得无法跟踪异常的原始出处。

java 代码
public void methodB()throws ExceptionB{   
   try{   
      methodA();   
      ……   
   }catch(ExceptionA ex){   
     throw new ExceptionB(ex);   
   }   
}  

   如上面的代码中,ExceptionB嵌套一个ExceptionA.我们暂且把ExceptionA称为“起因异常”,因为ExceptionA导致了ExceptionB的产生。这样才不使异常信息丢失。

 

所以我们在定义一个新的异常类时,必须提供这样一个可以包含嵌套异常的构造函数。并有一个私有成员来保存这个“起因异常”。
java 代码
public Class ExceptionB extends Exception{   
private Throwable cause;   
    
public ExceptionB(String msg, Throwable ex){   
super(msg);   
this.cause = ex;   
}   
    
public ExceptionB(String msg){   
super(msg);   
}   
    
public ExceptionB(Throwable ex){   
this.cause = ex;   
}   
}   
当然,我们在调用printStackTrace方法时,需要把所有的“起因异常”的信息也同时打印出来。所以我们需要覆写printStackTrace方法来显示全部的异常栈跟踪。包括嵌套异常的栈跟踪。
java 代码
public void printStackTrace(PrintStrean ps){   
if(cause == null){   
super.printStackTrace(ps);   
}else{   
ps.println(this);   
cause.printStackTrace(ps);   
}   
}   
一个完整的支持嵌套的checked异常类源码如下。我们在这里暂且把它叫做NestedException

java 代码
public NestedException extends Exception{   
private Throwable cause;   
public NestedException (String msg){   
super(msg);   
}   
    
public NestedException(String msg, Throwable ex){   
super(msg);   
This.cause = ex;   
}   
    
public Throwable getCause(){   
return (this.cause == null ? this :this.cause);   
}   
    
public getMessage(){   
String message = super.getMessage();   
Throwable cause = getCause();   
   if(cause != null){   
     message = message + “;nested Exception is ” + cause;   
   }   
return message;   
}   
public void printStackTrace(PrintStream ps){   
if(getCause == null){   
    super.printStackTrace(ps);   
       
}else{   
ps.println(this);   
getCause().printStackTrace(ps);   
}   
}   
    
public void printStackTrace(PrintWrite pw){   
if(getCause() == null){   
    super.printStackTrace(pw);   
}   
else{   
    pw.println(this);   
    getCause().printStackTrace(pw);   
}   
}   
public void printStackTrace(){   
printStackTrace(System.error);   
}   
}   
    
同样要设计一个unChecked异常类也与上面一样。只是需要继承RuntimeException。


4. 如何记录异常
    作为一个大型的应用系统都需要用日志文件来记录系统的运行,以便于跟踪和记录系统的运行情况。系统发生的异常理所当然的需要记录在日志系统中。
java 代码
public String getPassword(String userId)throws NoSuchUserException{   
UserInfo user = userDao.queryUserById(userId);   
If(user == null){   
Logger.info(“找不到该用户信息,userId=”+userId);   
throw new NoSuchUserException(“找不到该用户信息,userId=”+userId);   
}   
else{   
return user.getPassword();   
}   
}   
    
public void sendUserPassword(String userId) throws Exception {   
UserInfo user = null;   
try{   
user = getPassword(userId);   
   //……..   
sendMail();   
//   
}catch(NoSuchUserException ex)(   
   logger.error(“找不到该用户信息:”+userId+ex);   
   throw new Exception(ex);   
}   
我们注意到,一个错误被记录了两次.在错误的起源位置我们仅是以info级别进行记录。而在sendUserPassword方法中,我们还把整个异常信息都记录了。

笔者曾看到很多项目是这样记录异常的,不管三七二一,只有遇到异常就把整个异常全部记录下。如果一个异常被不断的封装抛出多次,那么就被记录了多次。那么异常倒底该在什么地方被记录?

异常应该在最初产生的位置记录!

如果必须捕获一个无法正确处理的异常,仅仅是把它封装成另外一种异常往上抛出。不必再次把已经被记录过的异常再次记录。

如果捕获到一个异常,但是这个异常是可以处理的。则无需要记录异常


java 代码
public Date getDate(String str){   
Date applyDate = null;   
SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);   
try{   
applyDate = format.parse(applyDateStr);   
}   
catch(ParseException ex){   
//乎略,当格式错误时,返回null   
}   
return applyDate;   
}   
捕获到一个未记录过的异常或外部系统异常时,应该记录异常的详细信息
java 代码
try{   
       ……   
        String sql=”select * from userinfo”;   
              Statement s = con.createStatement();   
              ……   
       Catch(SQLException sqlEx){   
          Logger.error(“sql执行错误”+sql+sqlEx);   
}   
   究竟在哪里记录异常信息,及怎么记录异常信息,可能是见仁见智的问题了。甚至有些系统让异常类一记录异常。当产生一个新异常对象时,异常信息就被自动记录。
java 代码
public class BusinessException extends Exception {   
      private void logTrace() {   
          StringBuffer buffer=new StringBuffer();   
          buffer.append("Business Error in Class: ");   
          buffer.append(getClassName());   
          buffer.append(",method: ");   
          buffer.append(getMethodName());   
          buffer.append(",messsage: ");   
          buffer.append(this.getMessage());   
          logger.error(buffer.toString());   
             
}   
public BusinessException(String s) {   
         super(s);   
Trace();   
}   
    这似乎看起来是十分美妙的,其实必然导致了异常被重复记录。同时违反了“类的职责分配原则”,是一种不好的设计。记录异常不属于异常类的行为,记录异常应该由专门的日志系统去做。并且异常的记录信息是不断变化的。我们在记录异常时应该给出更丰富些的信息。以利于我们能够根据异常信息找到问题的根源,以解决问题。

   虽然我们对记录异常讨论了很多,过多的强调这些反而使开发人员更为疑惑,一种好的方式是为系统提供一个异常处理框架。由框架来决定是否记录异常和怎么记录异常。而不是由普通程序员去决定。但是了解些还是有益的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值