异常处理反模式

本文翻译自Tim McCune 的《》

  应该抛出一个异常还是应该返回一个try/catch/printStackTrace()。当这些开发人员想要尝试更有新意的异常处理方式时,常常会陷入一些常见的异常处理反模式中。

  随着1998

异常的基本概念

  关于异常处理最重要的一个概念是了解在Java

  CheckedException类,并且是一种“咄咄逼人”(inyourface)的异常。一个checked

  UncheckedIllegalAccessException和NoSuchMethodException。一个unchecked

  Errors

创建自己的异常类

  大多数的软件包或系统组件应该包含自定义的异常类。有两种最主要的自定义异常的用法。

  一是当有问题发生时简单的抛出一个自定义异常,如:

thrownewMyObjectNotFoundException(“Couldn’tfindobjectid”+id);

  二是对某个异常进行包装然后抛出另一个异常,如:

catch(NoSuchMethodExceptione){thrownewMyServiceException(“Couldn’tprocessrequest”,e);}

  包装一个异常可以通过增加自己的消息来为用户提供额外的信息(见上述例子),同时保留了原来异常的堆栈跟踪。(译者注:如果使用的是直接抛出一个新的异常,那么堆栈就是从抛出的那一刻开始追踪,之前的异常来来源等信息就没有了)这种做法也能让你隐藏自己代码实现的细节,这是对异常进行包装的最重要的原因。例如HibernateException的各种子类中。使用这种方式可以让你改动模块的底层代码的时无需改动模块的公共API。

异常与事务(Transaction)

EJB2

EJB2

EJB3

为了在某种程度上缓解上述提到的回滚的问题,

@ApplicationException(rollback=true)publicclassFooExceptionextendsException…

消息驱动

需要注意的是,当使用队列驱动的消息驱动

记录日志(Logging)

  当遇到一个

  尽管有

  如果你在使用commons-logging或Log4j的话,要注意一个陷阱。在一个实现方式上,error,warn,info,和debug方法需要你提供两个参数,一个是消息的内容,服务器空间,一个是Throwable对象。如果是想要记录一个异常被抛出的情况,那么记得要传递两个参数。在另一个实现方式上,只接收一个参数,那么将exception对象传递给它,它会隐藏异常的跟踪堆栈。

  当调用log.debug()方法时,一种比较好的习惯是将它放在一个log.isDebugEnabled()检查块中。当然,这个建议纯粹是为了代码优化。这是一个值得养成的好习惯。

  不要使用System.out或System.err,而应该使用logger。Logger是可配置、灵活的,并且每一个输出目的地可以决定本次记录的严重程度(FATAL/ERROR/WARN/INFO/DEBUG)。向System.out打印一个消息是草率的,通常情况下这样的行为不可原谅。

反模式(antipatterns)

记录并抛出(logandthrow)

  例如

catch(NoSuchMethodExceptione){LOG.error(“Blah”,e);throwe;}

  或者

catch(NoSuchMethodExceptione){LOG.error(“Blah”,e);thrownewMyServiceException(“Blah”,e);}

  或者

catch(NoSuchMethodExceptione){e.printStackTrace();thrownewMyServiceException(“Blah”,e);}

  这三种方式都是错误的。这类方式是最讨人厌的错误处理反模式。要么记录一个异常,要么抛出一个异常,但不要同时进行“抛出”和“记录”两种操作。同时进行这两类操作会对同一个问题产生多种log消息,这会给运维人员分析日志带来麻烦。

抛出异常基类(ThrowingException)

  看下面这个例子:

  publicvoidfoo()throwsException{

  这样做是草率的,它完全违背了使用

ThrowingtheKitchenSink(这个不知道怎么翻译合适……)

  例如:

publicvoidfoo()throwsMyException,AnotherException,SomeOtherException,YetAnotherException{

  抛出多个

捕获异常基类(CatchingException)

  例如:

try{foo();}catch(Exceptione){LOG.error(“Foofailed”,e);}

  这通常是错误的和草率的。这种方式下捕获了原本应该被抛出的异常。捕获异常基类的问题在于,如果你随后要调用别的函数,而这个函数含有一个checkedException基类(甚至是Throwable类),那么你或许永远不知道你的代码里有本应该处理但却没有处理异常,这样一来你的代码是错误的而你却无从知晓(IDE不会提示,因为Exception基类被捕获了)。

破坏性的包装

  例子:

catch(NoSuchMethodExceptione){thrownewMyServiceException(“Blah:”+e.getMessage());}

  这种方式破坏了原本的异常对象e的追踪堆栈,使用这种包装方式你将无法追踪这个异常之前的传递路径。

记录并抛出

  例子:

catch(NoSuchMethodExceptione){LOG.error(“Blah”,e);returnnull;}

  或

赚钱之道很多,但是找不到赚钱的种子,便成不了事业家。

异常处理反模式

相关文章:

你感兴趣的文章:

标签云: