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
业务异常类后,就可以用它来封装原始异常,从而实现对异常的链式处理
。
总结
所谓异常链,就是在一个异常类的构造器中使用另一个异常类所谓参数.
原文链接: 10.4.5 异常链