使用 AOP 陷阱之一

    手头上的一个项目使用了Spring.net的AOP了处理程序的异常,一不小心就掉进了陷阱,这里记录下来,希望对后来者有所帮助。Spring.net的AOP实现是通过运行时创建动态的AOP代理来完成的。简单图示如下:

    图中的“POJO”以词来自Java,表示一个普通的.net对象,所有对POJO的调用都要经过Aop代理进行,因此Aop才有机会插入Pre_Action、Post_Action、和Around_Action。


    现在我以手上的工作举个例子,看我是如何掉入陷阱的:

    public interface IIrasRemotingServiceAccesser
    {
        
void Initialize() ;

        
void fsManager_AsServiceListChanged(int serverID, string serverName, ArrayList serviceList) ;
        
void CheckMySelf() ;
     }

    我们没有必要了解IIrasRemotingServiceAccesser接口的具体用途,只要了解这个接口将被Aop代理,为了使Aop能够截获fsManager_AsServiceListChanged方法,我把它放入了接口定义中,实际上它是一个事件处理函数,本应是私有的。
    Initialize方法使用fsManager_AsServiceListChanged预定了某个事件:

        public void Initialize()
        {                
            this.fsManager.AsServiceListChanged += new CbServiceChanged(fsManager_AsServiceListChanged);
        }

    运行程序后,发现CheckMySelf方法能正常被AOP截获,而当fsManager.AsServiceListChanged事件发生时,对fsManager_AsServiceListChanged的调用没有被截获。

    这是为什么?
    原来是预定事件时,绕过了AOP代理:

        public void Initialize()
        {                
            
this.fsManager.AsServiceListChanged += new CbServiceChanged(this.fsManager_AsServiceListChanged);
        }

    请注意红色的“this”,正是它绕过了AOP代理。所以fsManager_AsServiceListChanged的调用没有被截获。解决方案?我常用的有两种:
(1)将this换为代理:

        public void Initialize()
        {
            IIrasRemotingServiceAccesser myAopProxy 
= (IIrasRemotingServiceAccesser)MainClass.SpringContext.GetObject("irasRemotingServiceAccesser") ;
            
this.fsManager.AsServiceListChanged += new CbServiceChanged(myAopProxy.fsManager_AsServiceListChanged);
        }

    可以看到,myAopProxy.fsManager_AsServiceListChanged被委托为事件处理函数,这是通过Aop代理myAopProxy进行。这种方法解决了问题,但是又引入了新的问题--IIrasRemotingServiceAccesser实现类不再是一个“POJO”,而是依赖于Spring.net框架了。更好的办法是第二种:

(2)使用前文的Bridge方式。将IIrasRemotingServiceAccesser设计修改如下:

    public interface IIrasRemotingServiceAccesser
    {
        
void Initialize() ;

        
void ChangeServiceList(int serverID, string serverName, ArrayList serviceList) ;
        
void CheckMySelf() ;
    }

    然后通过Bridge将IIrasRemotingServiceAccesser 和 fsManager的事件桥接起来。(原理详见http://zhuweisky.cnblogs.com/archive/2005/12/20/301098.html
    如此,原来Initialize方法中的事件预定就转移到Bridge中了,所以就避免了绕过AOP代理的情况。



posted @   zhuweisky  阅读(1227)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述
点击右上角即可分享
微信分享提示