10.4.5 异常链
10.4.5 异常链
对于真实的企业级应用而言,常常有严格的分层关系,层与层之间有非常清晰的划分,上层功能的实现严格依赖于下层的API,也不会跨层访问。图10.5显示了这种具有分层结构应用的大致示意图。

对于一个采用图10.5所示结构的应用,当业务逻辑层访问持久层出现SQLException异常时,程序不应该把底层的SQLException异常传到用户界面,有如下两个原因。
- 对于正常用户而言,他们不想看到底层
SQLException异常,SQLException异常对他们使用该系统没有任何帮助。 - 对于恶意用户而言,将
SQLException异常暴露出来不安全。
异常转译
把底层的原始异常直接传给用户是一种不负责任的表现。通常的做法是:
程序先捕获原始异常,然后抛出一个新的业务异常,新的业务异常中包含了对用户的提示信息,这种处理方式被称为异常转译。假设程序需要实现工资计算的方法,则程序应该采用如下结构的代码来实现该方法。
1 | public calSal() throws SalException |
这种把原始异常信息隐藏起来,仅向上提供必要的异常提示信息的处理方式,可以保证底层异常不会扩散到表现层,可以避免向上暴露太多的实现细节,这完全符合面向对象的封装原则。
职责链模式
这种把捕获一个异常然后接着抛出另一个异常,并把原始异常信息保存下来的做法是一种典型的链式处理(23种设计模式之一:职责链模式),也被称为“异常链”。
在JDK1.4以前,程序员必须自己编写代码来保持原始异常信息。从JDK1.4以后,所有Throwable的子类在构造器中都可以接收一个cause对象作为参数。这个cause就用来表示原始异常,这样可以把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,你也能通过这个异常链追踪到异常最初发生的位置。例如希望通过上面的SalException去追踪到最原始的异常信息,则可以将该方法改写为如下形式。
1 | public calSal() throws SalException |
上面程序中的代码1,代码2在创建SalException对象时,传入了一个Exception对象,而不是传入了一个String对象,这就需要SalException类有相应的构造器。从JDK1.4以后, Throwable基类已有了一个可以接收Exception参数的方法,所以可以采用如下代码来定义SalException类。
1 | public class SalException extends Exception |
创建了这个SalException业务异常类后,就可以用它来封装原始异常,从而实现对异常的链式处理。
总结
要支持异常链,则自定义异常类需要创建三个构造器:
- 一个无参构造器,
- 一个带
String参数的构造器,用来传递异常信息 - 一个带
Throwable参数的构造器,用来传入原始的异常