第11章 AOP应用案例
本章内容
- 异常处理
- 安全检查
- 缓存
在结束有关SpringAOP的话题之前,有必要从最一般的层面上对适合使用AOP的场景做一个简单的介绍。如果称这些场景为AOP的最佳实践,其实未尝不可!
异常处理
或许你已经在使用AOP的方式进行异常处理,但是可能并不知道这实际上对应着一一个很有趣的术语,叫做Fault Barrier
。同样地,在接触这个术语之前,我实际上已经将这个概念所阐述的理念应用于工作中,直到看到dev2dev网站上的一篇文章“Effective
Java Exception”,才知道原来这种异常的处理方式还对应这么一个有趣的术语。
不过,在讲述Fault Barrier
之前,我们有必要先来回顾一下Java中异常处理的相关内容。
Java异常处理
异常处理是个很大的话题,限于篇幅,我们不可能详细讲述异常处理的方方面面。
下面只是简单地回顾一下Java中的异常类型和对应的处理方式,以便引出Fault Barrier
的概念。
Java不是最早也不是唯一使用异常机制的语言。不过,Java却在引入CheckedException
的基础上为其自身的异常处理添加了少许的新意。我们先来看一下Java中的异常层次体系大体上是一种什么样的结构,如图11-1所示。
在图11-1中,我们将Java中的异常类型分为如下两类。
- 通常将
java.lang.Error
和java.lang.RuntimeException
及其子类称之为unchecked exception
。之所以如此称呼,是 因为编译器不会对这些类型的异常进行编译期检查 。因为java.lang.Error
我们通常关心不着,所以,狭义上将java.lang.RuntimeException
及其子类暂且称为unchecked exception
也是可以的。 java.lang.Exception
及其子类,但除去java.lang.RuntimeException
分支,统称为checked exception
。一旦在程序的方法定义中声明了将会抛出“checked exception”,调用程序就必须对这些异常进行处理,而且 编译器会在编译期间对这些异常类型进行检查 。
各位大师级人物对checked exception和unchecked exception的论战暂且放在一边,我们主要讲述一下二者具体的应用场景。
-
unchecked exception通常对应系统中的严重异常情况,这些情况应用程序一般无法恢复,比如数据库挂掉、网线连接中断、服务器崩溃等。所以,unchecked exception所提供的信息 一般不是为应用程序准备的,而是为人准备的 ,确切地说,是为了能够让系统维护人员能够根据所提供的信息来判定到底哪里出了问题,以便人工干预。
-
checked exception引入Java后,一直是备受争议。不过,我觉得这与概念本身没有任何关系,是否被应用于合适的场合是由人来决定的,而不是概念本身。checked exception通常用于表明系统中的某些罕见的非正常状态。对于一个业务方法来说,使用错误号(ErrorCode)的时代是通过返回-1之类数字表明一些非正常状态,现在可以通过抛出不同类型的checked exception来表明这些非正常状态,并要求调用方对这些非正常状态进行处理,而编译器对checked exception的检查可以进一步加强这种关系。 通常checked exception是可恢复的 ,也是意料之中的, 它所提供的信息是面向应用程序的,应用程序可以根据系统逻辑对不同的checked exception类型进行有针对性的处理 。
在技术文章”Effective Java Exception”中,作者将unchecked exception
对应的情况称之为Fault
。而将checked exception
对应的情况称之为Contingency
(意外事故)。
而FaultBarrier要处理的,就是对应Fault
的情况,即unchecked exception。
FaultBarrier
对于checked exception来说,不同的类型可以有不同的处理方式,这完全是由系统逻辑来决定的,调用方可以根据不同的类型,有针对性地对checked exception进行处理。反过来,对于unchecked exception来说,不同的类型则是没有太多必要的,因为不管你的应用程序抛出何种类型的unchecked exception,最终都是需要人来进行干预,只要unchecked exception能够提供足够的信息,相应人员就可以进行处理,几乎就是无差别对待。
当系统中多个地方都可能抛出unchecked exception的时候,在引入Fault Barrier概念之前,我们可能会在每个调用的最项层,分别添加异常处理逻辑对其进行处理。而就像前面所说的那样,unchecked exception实际上可以做的事情很少,通常就是记录日志、通知相应人员。所以,这些相同的逻辑实现可以归并于一处进行处理,而不是让它们散落到系统的各处,也就是说,对于系统中的Fault来说,它实际上就是一种横切关注点(cross cutting concern)。
鉴于此,我们完全可以实现一个对应Fault处理的Aspect,让其对系统中的所有可能的Fault情况进行统一的处理。这个专职于处理Fault的Aspect,我们就称之为Fault Barrier。实际上,我们从讲解SpringAOP的ThrowsAdvice开始,就提供了一个FaultBarrier的实现实例。在该实例中,我们通过电子邮件的方式将系统中的Fault情况,也就是以unchecked exception形式给出的信息转发给相关人员,并记录到日志。当然,如果可能,你还可以加入更多的处理,比如分析unchecked exception信息、为相关人员提供更加友好的系统信息等。
安全检查
如果你已经使用Java开发Web应用程序多年,那么一定不会对Filter感到陌生吧?javax.servlet.Filter
是Servlet规范为我们提供的一种AOP支持,通过它,我们可以为基于Servlet的Web应用添加相应的资源访问控制(当然,还可以做很多其他事情)。
不过,基于Filter的Web应用的资源访问控制,仅仅是特定领域的安全检查需求。实际上,通过AOP,我们可以为任何类型的应用添加相应的安全支持。
在介绍AOP概念的时候就曾经提到过,安全检查属于系统的一种横切关注点,按照原先的方法进行系统开发,势必让这些安全检查逻辑散落系统各处,所以,对付它的最好办法就是用AOP。在将系统中可能需要安全检查的点排查清楚之后,我们就可以为这些点织入安全检查的逻辑了。
要为系统中某个点添加安全支持,最简单的办法就是提供一个拦截器,对所有访问该点的调用进行拦截。所以,对于 基本的安全检查的Aspect实现来说,如下方代码所示。
既然我们崇尚“不重新发明轮子”,在动手之前,有必要使用Google搜索一下是否有现成的,免去人力物力的浪费。
实际上,作为基于Spring平台的一套安全框架,Acegi框架(也就是最新的 Spring Security)已经可以说在企业级应用的安全领域声名远扬了。它在Spring基础上,提供了完备的系统认证、授权、访问控制等安全检查功能。Acegi框架最初是独立于Spring开发的,现在已经并入Spring Portfolio,更名为Spring Security,可以在http://www.acegisecurity.org/获得有关Acegi的更多信息。
缓存
AOP应用的另一个主要场景是为系统透明地添加缓存支持。缓存可以在很大程度上提升系统的性能,但它不是业务需求,而是系统需求。在现有方法论的基础上为系统添加缓存支持,就会因为系统中缓存需求的广泛分布,造成实现上的代码散落。
为了避免需要添加的缓存实现逻辑影响业务逻辑的实现,我们可以让缓存的实现独立于业务对象的实现之外,将系统中的缓存需求通过AOP的Aspect进行封装,只在系统中某个点确切需要缓存支持的情况下,才为其织入 。
使用AOP为系统添加缓存很简单,如下方代码所示。(代码示例没有添加同步逻辑,在生产环境下实现类似功能的时候需要考虑到这一点。)
在没有使用AOP之前,要为系统某个地方加入缓存的话,恐怕也是以差不多的逻辑实现的。不过,现在不需要这么做了,原因如下
- 现在已经有许多现成的Caching产品实现,如EhCache、JBossCache等。
- Spring Modules项目提供了对现有Caching产品的集成,这样就可以通过外部声明的方式为系统中的Joinpoint添加Caching支持。
本章小结
本章给出了几种常见的AOP应用场景(或最佳实践),以帮助读者扩展AOP的应用思路。但实际上,AOP的应用场景可以很广,而不只局限于本章罗列的这几种情况。关于AOP的更多应用场景和最佳实践,需要读者在实际开发中自己去挖据,去探索。