细节决定成败:业务拦截器
拦截器这个东西基本上是个面向方面的概念,因为只要是拦截器则必须依赖某个AOP框架来实现。可是偏偏我对.Net框架下的AOP框架颇存疑虑,包括我当初一直非常非常看好的Castle DynamicProxy以及基于Castle Dynamic Proxy的AspectSharp。我不是怀疑Castle,而是怀疑Microsoft;不是怀疑Microsoft的能力,而是怀疑Microsoft的开放性。记得我有一篇文章写过,在Asp.net中,如果要写一个HttpModule,无法在你的HttpModule中捕获Session的End事件。这件事对我打击很大。我的结论是,Microsoft制造这个“Bug”是故意的,其目的在于给第三方扩展框架制造麻烦。其理由是:一、这个Bug根本不是因为技术原因,没有任何技术上的理由;二、这个Bug非常非常浅,根本不象是偶然的Bug,而在相关的API文档中总是含糊其词,无法给试图扩充HttpModule者一个明确的解释。堵死这个洞以后,你所有的扩充都必须通过HttpApplication的派生类(例如Global)来实现,而不能通过Spring那样通过用户自定义配置来解决。产生这样的认识以后,我对Microsoft极其厌恶。我觉得所有基于Microsoft .Net Framework的第三方类库厂商,开源的或者商业化的,都必然没有出路。可惜我的客户没有厌恶Microsoft,我还必须被绑在这艘战船上可能会一直航行到沉没时为止。(这仅仅是我个人的看法,各位读者可别受影响)
我不能使用Remoting提供的AOP框架,除了性能考虑之外,架构也太复杂了一点。而我的拦截都是一些很简单的拦截,甚至谈不上真正的拦截。但由于是全局性的(所以不适合用Delegate),并且必须充分地摆脱对业务的依赖,所以必须能够非常简单地通过自定义配置来织入业务层所实现的拦截。
在我的框架中,用户自定义的拦截器,只要实现IInterceptor接口,然后将实现这个接口的实例通过向静态的AppContext类进行安装就可以达到织入拦截器的效果。由于不再需要额外的对象参与,因而隐藏了“方面”的概念,简化了拦截逻辑,性能上也不会因为拦截而受到影响。
这个拦截器接口这样定义:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](/Images/OutliningIndicators/None.gif)
很显然,每个方法都加入了当前AppContext参数,这样就很容易获取当前上下文的用户的身份,也就很容易获得所需要的权限集合。现在分别介绍一下这八个拦截方法。
加载前:有一些“方面”(例如权限)有机会加入一些约束。方法中的Constraint类型的参数是传址的,所以可以替换掉。也可以在加载前加载一些权限控制器之类的实体对象。
加载后:通知“方面”某个对象已载入,可用于某些“方面”动态生成DataSet。
插入对象前:有一些被“方面”控制的属性(这些属性很可能业务层根本是透明的)这时候有机会设置这些属性。例如加入时间戳的操作。
插入对象后:成功插入对象后,“方面”有机会进行一些必要的整理。
更新对象前:有一些“方面”(例如业务规则校对)有机会对修改的值进行修正,直至取消本次修改。
更新对象后:通知“方面”对象已成功被修改,可用来动态修改DataSet或者记入日志。
删除对象前:有一些“方面”(例如日志)需要确认该对象是否被删除,或者只将一个删除操作改为修改操作。例如可以将“删除客户信息”的操作修改为“隐藏客户信息”的操作。该接口方法返回一个布尔量,用于通知持久层是否需要取消这次操作。
删除对象后:通知“方面”对象已被删除,也可用来动态修改DataSet或者记入日志。