更确切地说,我们在比较AOP的拦截器和Decorator模式,它们非常相似,一些AOP框架本身就是使用Decorator模式来实现拦截器功能的。
过滤器是架构设计模式中比较常用的一种,几乎每个灵活动态系统都需要过滤器,特别是当我们的数据以内存状态出现时,过滤器无疑成为领域层的一个核心业务 逻辑,当然如果你还是使用面向数据库的编程模式,过滤器功能就被你用SQL语句的where语法给替代了,那么以下你可能不必再看,请浏览这篇文章:状态对象:数据库的替代者。
当我们在一个AOP框架下编程,经常会问自己,到底过滤器这个功能是应该做成拦截器还是
Decorator,当我在Jdon Framework下重写JiveJdon时就不只一次的问我自己。
其实这是一个分析模式中的过滤器实现问题,也是一个现实设计的问题:在Servlet Filter和职责链以及装饰模式Decorator和AOP几个方面如何选择?
选择标准无外乎软件的两个终极目标:简单和高质量;高质量是反映在细粒度方面,简单则是在进行设计实现时比较容易方便。
在粒度粗细方面,又有一个衡量指标:功能覆盖范围,也是一个scope,例如:如果功能需求要为某个类的方法实现实现过滤,但是,你使用一个Servlet Filter这样过滤器实现,虽然
它也能够实现我们的目标,但是它对所有的Servlet请求都进行过滤,这无疑范了杀鸡取卵的错误,造成系统性能上的损失。
以一个具体案例为例子:
我们在论坛显示时,希望动态过滤掉不应该出现的字眼,也就是在帖子显示之前,加载一个过滤器,对内容进行替换。
这里的Model是ForumMessage,而对应的Service则是ForumMessageService; ForumMessageService的getMessage可以获得帖子内容,这里我们在getMessage之前加一个过滤器;那么这里应该如何选择呢?
因为我在Jdon Framework这样的Ioc/AOP框架下实现,所以想到的是AOP,我们定义一个拦截器,拦截的对象是ForumMessageService的getMessage方法,进行配置文件配置即可。似乎很简单很酷,可是真的很美丽吗?
这里,我们从原理上这几个过滤器实现进行解析一下:
我这里举个例子,你想向处于同一个城市对方送达一个信件,这时有两种选择:
1.委托高效的TNT UPS等快递公司
2.亲自跑一趟。
这两种哪个效率最高?无疑是亲自跑一趟,可能半个小时或一个小时解决问题,但是你一旦委托第三方,我想,无论快递公司如何高效,肯定没有你自己解决快速。所以,当你能够自己处理把握时,亲自动手无疑最快的。
编码时也类似这样,如果你能在编码时,能够确定拦截那个类哪个方法,有什么比你直接写代码还能执行更快的呢?
AOP的拦截器实现原理也是这样,当然,正是有这种考虑,所以所有AOP框架内,只有 AspectJ性能是最好的,因为它在你编译代码时就将你的拦截意图实现了,这属于静态织入 weaving,这就类似你自己写代码实现,但是与你自己写代码不同的是,你自己不但要写拦截器,而且还要自己手工将拦截器语句插入被拦截的那段代码,现 在,使用AspectJ你就不用做后者了,这也是整合了AspectJ的Spring 2.0给我们带来的效能。所以,为了在编译代码时做手脚,就不能使用SUN的JDK原来的javac了,必须用他们自己特定的javac了。有得必有失 吧。
理解了AOP拦截器拦截的原理,你可能感慨:原来所有的动态AOP(不改换编译器的AOP框架)拦截效能没有Decorator直接指定要快啊。所以, 如果你在编码设计阶段,可以知道你所要拦截的方法,那么,无疑直接使用Decorator模式组成过滤器是一种好方式。
这也是我先期不怎么看好使用动态AOP实现的Spring 1.X版本,也不太热衷于同样使用
动态AOP实现、虽符合EJB 3.0规范的JBoss 4.0了。可是,为什么总是有人迫不及待地告别EJB
2.x,夸张Spring,然后又狂炫EJB 3.0。在接纳他们之前,先把盒子打开看看验货一下,做一个普通的消费者应有的理性。
当然,不是说动态AOP没有用武之地,它类似SOA的集成作用,可以在不用修改原来代码结构上强行加入过滤器。而且AOP不只是拦截器,还有introduction,这一神奇功能可以突破Java单继承法则,显得象儿童世界里面的神奇魔法一样。
从某个方面来说,动态AOP和职责链非常类似,职责链类似击鼓传花,更像典型的打太极拳,推来推去,这也是政府效率不高的主要原因,同样,用在软件系统 里,性能最差。想象一下,当这朵花传到真正主人面前时,而这个主人是传花环节中最后一个,这种效率和直接抛绣球一样,直接将花抛给真正主人相比,无疑最耗 时间的,所以击鼓传花游戏就是玩的这个耗时间,在鼓声中,消耗时间,煎熬折磨你的心思,它玩的不是要把花如何快速准确地送给某人,它的目的性不确切。
我们的软件系统不能这么玩吧?
总结如下:过滤器实现方式在不保证功能前提下,从性能角度考虑有如下先后顺序:Decorator或Proxy模式;AOP拦截器。
考虑使用AOP拦截器时,最好选择那些受众面积比较广的功能,例如一些基础通用功能:权限检查;事务机制;Pool等,这些功能不是针对某个具体类或方 法(方法权限除外),而是一系列类,这样使用动态AOP拦截器,就是有些性能损耗也是值得的,而且是必要的,使用其他方法也会引起这样的损耗。
如果过滤器是业务逻辑的一部分,而且在设计时,我们可以确定这些过滤器,这样我们使用Decorator模式或Proxy模式进行特定指定的拦截,当 然,因为每个类/接口都需要一个附加的Decorator/Proxy,如果某个过滤功能是很多类都需要的,会形成很多Decorator/Proxy附 加类,当点形成面时,这时AOP切面概念就应该浮现在你脑海,这时升级使用AOP拦截器就更好。Decorator/Proxy在点上针对性相当强,特别 在这个点上有一系列过滤器需要实现时。
职责链和Decorator/AOP拦截器是有些 区别的,在一个动态运行系统中,有两个概念:由客户端触发的请求对象,该请求对象需要穿透一系列过滤器(防火墙),最终可能达到持久层数据库。 Decorator/AOP拦截器是对过滤器管理的一种模式,也就是说:怎么设计过滤器类;过滤器类关系是怎样;而职责链不是对类关系管理定义,而是为了 处理某个请求对象而实现的。他们区别在于目标对象不一样,所以职责链是一种很具体的行为。
在这个层面上,Command模式和其是相竞争的,Command模式类似直接抛绣球,知道目的,能够最有效率,但是前提在设计编码阶段你必须知道你的目的地;Command模式和职责链的区别与Decorator和AOP拦截器的区别是类似的。