拦截器这个东西基本上是个面向方面的概念,因为只要是拦截器则必须依赖某个 AOP 框架来实现。可是偏偏我对 .Net 框架下的 AOP 框架颇存疑虑,包括我当初一直非常非常看好的 Castle DynamicProxy 以及基于 Castle Dynamic ProxyAspectSharp 。我不是怀疑 Castle, 而是怀疑 Microsoft; 不是怀疑 Microsoft 的能力,而是怀疑 Microsoft 的开放性。记得我有一篇 文章 写过,在 Asp.net 中,如果要写一个 HttpModule ,无法在你的 HttpModule 中捕获 SessionEnd 事件。这件事对我打击很大。我的结论是, Microsoft 制造这个“ Bug ”是故意的,其目的在于给第三方扩展框架制造麻烦。其理由是:一、这个 Bug 根本不是因为技术原因,没有任何技术上的理由;二、这个 Bug 非常非常浅,根本不象是偶然的 Bug ,而在相关的 API 文档中总是含糊其词,无法给试图扩充 HttpModule 者一个明确的解释。堵死这个洞以后,你所有的扩充都必须通过 HttpApplication 的派生类 ( 例如 Global) 来实现,而不能通过Spring那样通过用户自定义配置来解决。产生这样的认识以后,我对 Microsoft 极其厌恶。我觉得所有基于 Microsoft .Net Framework 的第三方类库厂商,开源的或者商业化的,都必然没有出路。可惜我的客户没有厌恶 Microsoft ,我还必须被绑在这艘战船上可能会一直航行到沉没时为止。 ( 这仅仅是我个人的看法,各位读者可别受影响 )
我不能使用 Remoting 提供的 AOP 框架,除了性能考虑之外,架构也太复杂了一点。而我的拦截都是一些很简单的拦截,甚至谈不上真正的拦截。但由于是全局性的 ( 所以不适合用 Delegate) ,并且必须充分地摆脱对业务的依赖,所以必须能够非常简单地通过自定义配置来织入业务层所实现的拦截。
在我的框架中,用户自定义的拦截器,只要实现 IInterceptor 接口,然后将实现这个接口的实例通过向静态的 AppContext 类进行安装就可以达到织入拦截器的效果。由于不再需要额外的对象参与,因而隐藏了“方面”的概念,简化了拦截逻辑,性能上也不会因为拦截而受到影响。
这个拦截器接口这样定义:
None.gif      public   interface  IInterceptor
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
void BeforeLoad(AppContext context, Type loadType, ref Constraint constraint);
InBlock.gif        
void PostLoad(AppContext context, object obj);
InBlock.gif        
void BeforeInsert(AppContext context, object obj);
InBlock.gif        
void PostInsert(AppContext context, object obj);
InBlock.gif        
void BeforeUpdate(AppContext context, object obj);
InBlock.gif        
void PostUpdate(AppContext context, object obj);
InBlock.gif        
bool BeforeDelete(AppContext context, object obj);
InBlock.gif        
void PostDelete(AppContext context, object obj);
ExpandedBlockEnd.gif    }

None.gif

很显然,每个方法都加入了当前 AppContext 参数,这样就很容易获取当前上下文的用户的身份,也就很容易获得所需要的权限集合。现在分别介绍一下这八个拦截方法。
加载前 :有一些“方面” ( 例如权限 ) 有机会加入一些约束。方法中的 Constraint 类型的参数是传址的,所以可以替换掉。也可以在加载前加载一些权限控制器之类的实体对象。
加载后 :通知“方面”某个对象已载入,可用于某些“方面”动态生成 DataSet
插入对象前 :有一些被“方面”控制的属性 ( 这些属性很可能业务层根本是透明的 ) 这时候有机会设置这些属性。例如加入时间戳的操作。
插入对象后 :成功插入对象后,“方面”有机会进行一些必要的整理。
更新对象前 :有一些“方面” ( 例如业务规则校对 ) 有机会对修改的值进行修正,直至取消本次修改。
更新对象后 :通知“方面”对象已成功被修改,可用来动态修改 DataSet 或者记入日志。
删除对象前 :有一些“方面” ( 例如日志 ) 需要确认该对象是否被删除,或者只将一个删除操作改为修改操作。例如可以将“删除客户信息”的操作修改为“隐藏客户信息”的操作。该接口方法返回一个布尔量,用于通知持久层是否需要取消这次操作。
删除对象后 :通知“方面”对象已被删除,也可用来动态修改 DataSet 或者记入日志。