EnterpriseServerBase的AOP--EsbAOP实现

news/2025/2/23 22:05:22

    EsbAOP是EnterpriseServerBase类库中的轻量级AOP框架,它实现了AOP的主要思想--对方法调用进行截获,并加入自定义的预处理、后处理。 EsbAOP与其它很多开源的AOP实现有些不同,其不同之处主要在于EsbAOP并没有严格的实现AOP理论的各种设施,但是EsbAOP非常实用,在后续的文章中,大家可以看到EsbAOP是如何运用于实际的系统开发中的。

    理解EsbAOP系列文章需要Remoting和消息方面的知识作为基础,如果你以前没有接触过这些内容,可以阅读《.NET 本质论》的第七章。下面就来开始我们的EsbAOP之旅吧。

    既然AOP的核心思想是对方法调用进行截获,并加入自定义的预处理、后处理,那么我们可以很直观的定义出IAspect(方面)接口。而“特定方面”就是实现了IAspect接口的类,什么是“特定方面”?“特定方面”就是完成某一特定目的的一个Aspect,比如权限方面的特定目的就是进行权限管理。即,一个特定的方面,就代表了一个特定目的的前处理和后处理!

    public   interface  IAspect
    {    
        
void  PreProcess(IMethodCallMessage requestMsg , object  aspectClassArgument , object  aspectMethodArgument) ;
            
        
void  PostProcess(IMethodCallMessage requestMsg , ref  IMethodReturnMessage respond , object  aspectClassArgument , object  aspectMethodArgument) ;    
    }

   
    其中参数requestMsg表示方法调用的请求消息,而respond表示方法调用的结果。注意,如果目标方法在运行的过程中抛出了一个异常,那么该异常也表现为一个IMethodReturnMessage消息。注意,respond参数加上了ref关键字,表示EsbAOP不仅可以截获方法调用,而且还能改变方法运行的结果,这是多么神奇的功能啊!
    另外还有两个参数aspectClassArgument和aspectMethodArgument,为了更方便地说明它们的作用,非得举个例子不可,如下一个应用权限Aspect的例子。

    [Aspect( typeof (PermissionAspectWrap)  )]        
    
public   class  Example :ContextBoundObject
    {        
        
       
 [AspectSwitcher( typeof (PermissionAspectWrap) , true  ,Permission.Super)]        
        
public   void  SayHello( string  name)
        {
            Console.WriteLine(
" Hello , "   +  name) ;
        }
   
 }

    方法上有个AspectSwitcher特性,它的第三个参数就是aspectMethodArgument。在本例中,这个参数规定了调用SayHello方法所需要的权限是Super。而aspectClassArgument参数位于PermissionAspectWrap的包装之中,它主要用于特定的方面(本例中是PermissionAspect)在执行预处理/后处理时所需要的设施,比如本例中,PermissionAspect就需要通过aspectClassArgument传入一个IPermissionVerifier引用,这个IPermissionVerifier用于对权限是否满足进行判断。
    看了这段描述,不知道你对这两个参数有点了解了没有,不是太明白也没关系,对它们的认识后面会慢慢清晰起来的。
    
    上面Example类上用的“Aspect”特性,这个特性的定义如下:

    ///   <summary>
    
///  AspectAttribute 把被修饰类的实例委托给代理AspectChainProxy ,如此可以截获被修饰类的方法调用    
   
///   </summary>
    [AttributeUsage(AttributeTargets.Class ,AllowMultiple  =   false )]
    
public   class  AspectAttribute : ProxyAttribute
    {
        
private  Type[] theAspectProcessorWrapTypes  =   null  ;    

        
public  AspectAttribute( params  Type[] wrapTypes)
        {            
            
this .theAspectProcessorWrapTypes  =  wrapTypes ;
        }

        
#region  CreateInstance
        
///   <summary>
        
///     获得目标对象的自定义透明代理,该方法由系统调用
        
///   </summary>
         public   override  MarshalByRefObject CreateInstance(Type serverType) // serverType是被AopProxyAttribute修饰的类
        {
            
// 未初始化的实例的默认透明代理
            MarshalByRefObject target  =    base .CreateInstance (serverType);  // 得到未初始化的实例(ctor未执行)
             object [] args  =  {target ,serverType} ;
           
            
            
// 得到自定义的真实代理
            RealProxy rp  =   new  AspectChainProxy(target ,serverType , this .theAspectProcessorWrapTypes) ; // new AopControlProxy(target ,serverType) ;
             return  (MarshalByRefObject)rp.GetTransparentProxy() ;
        }
        
#endregion
    }    


    可以看到,该特性的主要任务是把目标实例委托给AspectChainProxy代理,使截获方法调用得以顺利切入!AspectAttribute的构造参数由params修饰,表示其可接受多个参数,每一个参数对应这一个特定目的的方面(最终,AspectAttribute把这些参数传给了AspectChainProxy代理),这表示前面的例子中可以这样:

    [AspectAttribute( typeof (PermissionAspectWrap) , typeof (ExceptionLoggerAspectWrap))]        
    
public   class  Example :ContextBoundObject

    这样就在Example类上运用了两个方面,一个用于权限管理,一个用于异常日志。

    AspectChainProxy代理用于管理所有运用于目标类(如上面的Example类)上的所有方面,使在运行时,CLR能调用所有方面的前处理和后处理,AspectChainProxy实现如下:

    public   class  AspectChainProxy: RealProxy 
    {
        
private  MarshalByRefObject target            =   null ;
        
private  Type[] theAspectProcessorWrapTypes  =   null ;
        
private  ArrayList aspectCallerList          =   new  ArrayList() ; // 集合中为AspectCaller实例
    

        
public  AspectChainProxy(MarshalByRefObject target ,Type serverType , params  Type[] aopProcessorWrapTypes):  base (serverType)
        {
            
this .target  =  target;
            
this .theAspectProcessorWrapTypes  =  aopProcessorWrapTypes ;            
        }

        
#region  Invoke
        
public   override  IMessage Invoke(IMessage msg)
        {                    
            IMethodCallMessage call 
=  (IMethodCallMessage)msg ;
            
this .FillAspectCallerList(call) ;                

            
// 如果触发的是构造函数,此时target的构建还未开始
            IConstructionCallMessage ctor  =  call  as  IConstructionCallMessage ;
            
if (ctor  !=   null )
            {
                
// 获取最底层的默认真实代理
                RealProxy default_proxy  =  RemotingServices.GetRealProxy( this .target) ;

                default_proxy.InitializeServerObject(ctor) ;
                MarshalByRefObject tp 
=  (MarshalByRefObject) this .GetTransparentProxy() ;  // 自定义的透明代理 this

                
return  EnterpriseServicesHelper.CreateConstructionReturnMessage(ctor,tp);                
            }    
        
            
this .PreProcess(call) ;
            
            IMethodReturnMessage result_msg 
=  RemotingServices.ExecuteMessage( this .target ,call) ;  // 将消息转化为堆栈,并执行目标方法,方法完成后,再将堆栈转化为消息
            
            
this .PostProcess(call , ref  result_msg) ;
            
            
return  result_msg ; 
        }    
        
#endregion

        
#region  FillAspectCallerList
        
private   void  FillAspectCallerList(IMethodCallMessage call)
        {
            
this .aspectCallerList.Clear() ;

            
if ( this .theAspectProcessorWrapTypes  ==   null )
            {
                
return  ;
            }

            
// 显式启动了方面的WrapType
            ArrayList overtWrapTypeList  =   new  ArrayList() ;
            

            
// 查询目标方法是否 "显式" 启用AOP的MethodAopSwitcherAttribute
             foreach (Attribute attr  in  call.MethodBase.GetCustomAttributes( false ))
            {
                AspectSwitcherAttribute aspectSwitcher 
=  attr  as  AspectSwitcherAttribute ;
                
if (aspectSwitcher  ==   null )
                {
                    
continue  ;
                }                

                
if (aspectSwitcher.DestAspectProcessorWrapType  ==   null )
                {
                    
continue  ;
                }    

                overtWrapTypeList.Add(aspectSwitcher.DestAspectProcessorWrapType) ;

                
if  ( !  aspectSwitcher.UseAspect)
                {
                    
continue  ;
                }

                IAspectProcessorWrap processorWrap 
=    this .GetAspectProcessorWrap(aspectSwitcher.DestAspectProcessorWrapType);
                
if (processorWrap  ==   null )
                {
                    
continue  ;
                }

                AspectCaller caller         
=   new  AspectCaller() ;
                caller.AspectMethodArgument 
=  aspectSwitcher.AopArgument ;
                caller.CurProcessorWrap     
=  processorWrap ;

                
this .aspectCallerList.Add(caller) ;
            }

            
// 非显式启用的方面
             foreach (Type wrapType  in   this .theAspectProcessorWrapTypes)
            {
                
bool  passIt  =   this .WrapTypeIsInOvertWrapTypeList(wrapType ,overtWrapTypeList) ;

                
if ( !  passIt)
                {
                    IAspectProcessorWrap processorWrap 
=  (IAspectProcessorWrap)Activator.CreateInstance(wrapType) ;
                    
if (processorWrap.DefaultAspectSwitcherState  ==  AspectSwitcherState.On)
                    {
                        AspectCaller caller         
=   new  AspectCaller() ;                        
                        caller.CurProcessorWrap     
=  processorWrap ;

                        
this .aspectCallerList.Add(caller) ;
                    }
                }
            }
        }

        
private   bool  WrapTypeIsInOvertWrapTypeList(Type wrapType ,ArrayList overtWrapTypeList)
        {
            
foreach (Type tempWrapType  in  overtWrapTypeList)
            {
                
if (wrapType  ==  tempWrapType)
                {
                    
return   true  ;
                }
            }

            
return   false  ;
        }
        
#endregion
    
        
#region  GetAspectProcessorWrap
        
private  IAspectProcessorWrap GetAspectProcessorWrap(Type aspectProcesserWrapType)
        {
            
foreach (Type wrapType  in   this .theAspectProcessorWrapTypes)
            {
                
if (wrapType  ==  aspectProcesserWrapType)
                {
                    IAspectProcessorWrap wrap 
=  (IAspectProcessorWrap)Activator.CreateInstance(wrapType) ;
                    
return  wrap ;
                }
            }

            
return   null  ;
        }
        
#endregion

        
#region  PreProcess ,PostProcess
        
private   void  PreProcess(IMethodCallMessage requestMsg)
        {
            
foreach (AspectCaller caller  in   this .aspectCallerList)
            {
                IAspect aspectProcessor 
=  (IAspect)Activator.CreateInstance(caller.CurProcessorWrap.AspectProcessorType) ;                
                
if (aspectProcessor  !=   null )
                {                    
                    aspectProcessor.PreProcess(requestMsg ,caller.CurProcessorWrap.AspectClassArgument ,caller.AspectMethodArgument) ;
                }
            }
        }

        
private   void  PostProcess(IMethodCallMessage requestMsg,  ref  IMethodReturnMessage respond)
        {
            
foreach (AspectCaller caller  in   this .aspectCallerList)
            {
                IAspect aspectProcessor 
=  (IAspect)Activator.CreateInstance(caller.CurProcessorWrap.AspectProcessorType) ;
                
if (aspectProcessor  !=   null )
                {                    
                    aspectProcessor.PostProcess(requestMsg ,
ref  respond ,caller.CurProcessorWrap.AspectClassArgument ,caller.AspectMethodArgument) ;
                }
            }
        }
        
#endregion         
    }
    
    
///   <summary>
    
///  AspectCaller 针对某一特定的方法,实施的一次Aspect调用
    
///   </summary>
     public   class  AspectCaller
    {
        
public  IAspectProcessorWrap CurProcessorWrap      =   null  ;
        
public   object                AspectMethodArgument  =   null  ;
    }


    AspectChainProxy的第四个构造参数正是从AspectAttribute传递过来的,这个参数包含了运用于目标类的所有特定方面的相关信息。AspectChainProxy的核心方法是Invoke方法,该方法由CLR在进入截获阶段时调用。Invoke方法的流程一目了然。
    上文已经有几个地方已经涉及了IAspectProcessorWrap 接口了,正如其名,它是一个包装,包装中的主要内容是一个Asepect,以及提供与该Aspect相关的其它信息。

///   <summary>
    
///  IAspectProcessorWrap 对某一Aspect类型和对应的AspectClassArgument进行封装
    
///   </summary>
     public   interface  IAspectProcessorWrap
    {
        Type   AspectProcessorType{
get  ;}  // 返回的是IAspect的实现
         object  AspectClassArgument{ get  ;}
        
        
///   <summary>
        
///  当一个方法没有被某个方面的AspectSwitcherAttribute修饰时,是否启用该方面
        
///   </summary>
        AspectSwitcherState DefaultAspectSwitcherState{ get  ;} 
    }


    注释已经很好的解释了IAspectProcessorWrap 接口的方方面面,到这里,你也许发现了,AspectAttribute的构造参数就是实现了IAspectProcessorWrap 的类型。一个特定目的方面(如PermissionAspect)就对应着一个包装(PermissionAspectWrap)。

    关于这个轻量级的AOP实现,还有一个基础设施没有介绍,那就是AspectSwitcherAttribute,这也是一个特性,主要用于修饰方法。它的目的有两个:
(1)决定被修饰方法针对某特定方面是否启用截获。比如当Example类上使用权限Aspect时,对每个方法的调用都将进行权限判断,然而有些方法调用可能是不需要权限判断的,这种一锅端的做法存在很多AOP实现中,而EsbAOP通过AspectSwitcherAttribute很好的解决了这个问题。并且,IAspectProcessorWrap 接口的DefaultAspectSwitcherState属性更是对此支持的增强。
(2)传递前述的aspectMethodArgument给预处理和后处理。这个前面已经见过了。

    AspectSwitcherAttribute的定义如下:

    [AttributeUsage(AttributeTargets.Method ,AllowMultiple  =   true  )]
    
public   class  AspectSwitcherAttribute : Attribute
    {
        
private   bool    useAspect  =   false  ;
        
private   object  theAopArgument  =   null  ;
        
private  Type   destAspectProcessorWrapType  =   null  ;

        
public  AspectSwitcherAttribute(Type destAspectWrapType , bool  useAop)
        {                    
            
this .destAspectProcessorWrapType  =  destAspectWrapType ;
            
this .useAspect                      =  useAop ;
        }

        
public  AspectSwitcherAttribute(Type destAspectWrapType , bool  useAop , object  aopArg)
        {            
            
this .useAspect                      =  useAop ;
            
this .theAopArgument                  =  aopArg ;
            
this .destAspectProcessorWrapType  =  destAspectWrapType ;
        }

        
public   bool  UseAspect
        {
            
get
            {
                
return   this .useAspect ;
            }
        }

        
public   object  AopArgument
        {
            
get
            {
                
return   this .theAopArgument ;
            }
        }

        
public  Type DestAspectProcessorWrapType
        {
            
get
            {
                
return   this .destAspectProcessorWrapType ;
            }
        }
    }

    
public   enum  AspectSwitcherState
    {
        On ,Off
    }


    EsbAOP的所有实现就介绍完了,如果讲述有不清楚的地方,请留言,我会根据反馈进行修改。在后续的文章中,将在EsbAOP的基础上开展AOP应用,比如权限管理、异常日志、异常关闭器......。

 

 


http://www.niftyadmin.cn/n/989480.html

相关文章

用laravel搭一个微信公众号后台

我使用的是laravel5.2, 早期版本可能不适合下面的方法。 在routes.php写下接收微信服务器post请求的路径: Route::post(wechatmp, WechatControllerresponseMsg);在App\Http\Middleware\VerifyCsrfToken里&#xff0c;将该请求路径去除CSRF TOKEN的保护&#xff0c;官网说明&a…

JSON数据从MongoDB迁移到MaxCompute最佳实践

摘要&#xff1a; 本文为您介绍如何利用DataWorks数据集成直接从MongoDB提取JSON字段到MaxCompute。 数据及账号准备首先您需要将数据上传至您的MongoDB数据库。本例中使用阿里云的云数据库 MongoDB 版&#xff0c;网络类型为VPC&#xff08;需申请公网地址&#xff0c;否则无法…

天才们为什么独身一世?

蜀道之难&#xff0c;难于上青天。如果有人问&#xff1a;有什么比蜀道还难&#xff1f;老孙会说&#xff1a;思维。从一种思维到另一种思维之间的“蜀道”&#xff0c;更加艰难&#xff01;男人和女人之间&#xff0c;就有那么一条蜀道。畅销书《男人来自火星&#xff0c;女人…

深入理解MYSQL undo redo

undo log保证事务的原子性&#xff08;回滚&#xff09; A、BeginB、记录A1到undo log中C、修改记录A3D、记录B1到undo log中E、修改记录B2F、写入undo log到磁盘中G、写入数据到磁盘中H、Commit 复制代码A-E步骤都是在内存中完成 A-F之间如果出现问题&#xff0c;由于undo log…

[编程题]圈地运动

[编程题]圈地运动 圈地运动&#xff0c;就是用很多木棍摆在地上组成一个面积大于0的多边形&#xff5e; 小明喜欢圈地运动&#xff0c;于是他需要去小红店里面买一些木棍&#xff0c;期望圈出一块地来。小红想挑战一下小明&#xff0c;所以给小明设置了一些障碍。障碍分别是&a…

从现在开始要学习Python了

Python是个好东东&#xff0c;即没有C和C烦人的指针问题&#xff0c;功能似乎也比PHP要强不少&#xff0c;再重要的是它是纯粹的面向对象语言&#xff0c;语句清晰&#xff0c;容易理解&#xff0c;这对于学习来说有着很大的优势&#xff0c;学习语言的初期都是看别人代码的&am…

动态规划之 筷子

描述 A 先生有很多双筷子。确切的说应该是很多根&#xff0c;因为筷子的长度不一&#xff0c;很难判断出哪两根是一双的。这天&#xff0c;A 先生家里来了K 个客人&#xff0c;A 先生留下他们吃晚饭。加上A 先生&#xff0c;A夫人和他们的孩子小A&#xff0c;共K3个人。每人需要…

动态规划优化

状态优化 bzoj2064 分裂 存在通解&#xff1a;把原始集合都合并&#xff0c;再一一拆开。 如果可以划分一些集合&#xff0c;使得原始集合和目标集合对应的小集合相等&#xff0c;那么可以节省操作次数。 ans(n1-1)(n2-1)-2*(x-1) x为划分的相同集合数。 n<10,状压 另外&…