一个故障屏障组件的第一要务就是记录下故障异常中包含的信息以为将来所用。到现在为止,一个应用日志是做成此事的首选。异常的嵌套的信息,栈日志,等等,都是对诊断有价值的信息。传递故障信息最差的地方是通过用户界面。把应用的使用者卷进查错的进程对你,对你的用户而言都不好。如果你真的很想把诊断信息放上用户界面,那可能意味着你的日志策略需要改进。
故障屏障的下一个要务是以一种可控的方式来结束操作。这具体的意义要取决于你应用的设计,但通常包括产生一个可通用的回应给可能正在等待的客户端。如果你的应用是一个Web service,这就意味着在回应中用soap:Server的
在一个Struts的应用里,你的故障屏障会以一种全局异常处理器的形式出现,并被配置成处理RuntimeException的任何子类。你的故障屏障类将延伸 org.apache.struts.action.ExceptionHandler类,必要的话,重写它的方法来实施用户自己的特别处理。这样就会处理好不小心产生的故障情况和在处理一个Struts动作时发现的故障。图2显示的就是应变和故障异常。
图2 应变和故障异常
如果你使用的是Spring MVC架构,你可以继承SimpleMappingExceptionResolver类,并配置成处理RuntimeException和它的子类们,这样很容易的就建起了故障屏障。通过重写resolveException的方法,你可以在使用父类的方法来把需求导引到一个发出通用错误提示的view 组件之前,加入你需要的用户化的处理。
当你的架构包含了故障屏障,程序员都知晓了后,再写出一次性的故障异常的冲动就会锐减。结果就是应用中出现更干净,更易于维护的代码。
架构中应变的处理
将故障处理交与屏障后,主要组件间的应变交流变得容易多了。一个应变代表着与主要返回结果同等重要的另外一种方法结果。因此,检查的异常类型是一个能够很好地传递应变情况的存在并提供必要的信息来与它竞争的工具。这个方式借助于Java编译器的帮助来提醒程序员关于他们所用的API的方方面面以及提供全套的方法输出的必要性。
仅仅使用方法的返回值类型来传递简单的应变是可能的。比如,返回一个空引用,而不是一个具体的对象,可以意味着对象由于一个已定义的原因不能被建立。Java I/O的方法通常返回一个整数值-1,而不是字节的值或字节的数来表示文件的结尾。如果你的方法的语义简单到可以允许的地步,另一种返回值的方法是可以使用的,因为它摒弃了异常带来的额外的花销。不足之处是方法的调用方要检测一下返回的值来判断是主要结果,还是应变结果。但是,编译器没有办法来保证方法调用者会使用这个判断。
如果一个方法有一个void的返回类型,异常是唯一的方法来表示应变发生了。如果一个方法返回的是一个对象的引用,那么返回值只可能是空或非空(null and non-null)。如果一个方法返回一个整数型,选择与主要返回值不冲突的,可以表示多种应变情况的数值是可能的。但是这样的话,我们就进入了错误代码检查的世界,而这正式Java异常模式所着力避免的。
提供一些有用的信息
定义不同的故障报告的异常类型是没什么道理的,因为故障屏障对所有异常类型一视同仁。应变异常就有很大的不同,因为它们的原意是要向方法调用者传递各种情况。你的架构可能会指出这些异常应该继承java.lang.Exception或一个指定的基类。
不要忘记你的异常应该是百分百的Java类型,你可以用它来存放为你的特殊目的服务的特殊字段,方法,甚至是构造器。比如,被假想的processCheck()方法抛出的 InsufficientFundsException这个异常类型就应该包含着一个OverdraftProtection的对象,它能够从另外一个帐户里把短缺的资金转过来。
日志还是不要日志
记录下故障异常是有用处的,因为日志的目的是在一些需要改正的情况下,日志可以吸引人们的注意力。但对应变异常而言却并非如此。应变异常可能代表的只是极少数情况,但是在你的应用里,每一个情况还是会发生的。它们意味着你的应用正在如最初的设计般正常工作着。经常把日志代码加进应变的捕获块里会使你的代码晦涩难懂,而又没有实际的好处。如果一个应变代表了一重要的事件,在抛出一个异常应变来警醒调用者之前,产生一笔日志,记录下这个事件可能会让这个方法更好些。
异常的各个方面
在Aspect Oriented Programming(AOP)的术语里,故障和应变的处理是互相渗透的问题。比如,要实施故障屏障的模式,所有参与的类必须遵循通用规格:
故障屏障方法必须存活在遍历参与类的方法调用图的最前端
参与类必须使用未检查的异常来表示故障情况
参与类必须使用故障屏障期望得到的有针对性的未检查的异常类型
参与类必须捕获并从低端方法中把在执行情境下注定的故障转换成检查的异常
参与类不能干扰故障异常被传递到故障屏障的过程
这些问题超越了那些本不相干的类的边界。结果就是少数零散的故障处理代码,以及屏障类和参与类间暗含的耦合(这已经比不使用模式进步多了!)。 AOP让故障处理的问题被封装在通用的可以作用到参与类的层面上。如AspectJ和Spring AOP这样的Java AOP架构认为异常的处理是添加故障处理行为的切入点。这样,把参与者绑定在故障屏障的模式可以放松些。故障的处理可以存活在一个独立的,不相干的方面里,从而摒弃了屏障方法需要放在方法激活次序的最前头的要求。
如果在你的架构里利用了AOP,故障和应变的处理是理想的在应用里用到的在方面上的候选。对故障和应变的处理在AOP架构下的使用做一个完整的勘探将是将来论文里一个很有意思的题目。
结论
虽然Java异常模型自它出现以来就激发了热烈的讨论,如果使用正确的话,它的价值还是很大的。作为一个设计师,你的任务是建立好规格来最大限度地利用好这个模型。以故障和应变的方式来考量异常可以帮助你做出正确的决定。合理使用好Java异常模型可以让你的应用简单,易维护,和正确。AOP技术将故障和应变定位为相互渗透的问题,这个方法可能会对你的架构提供一些帮助。
作者“ERDP技术架构”