代码改变世界

SqlCenter中的AOP实现

2009-02-05 23:59  宝宝合凤凰  阅读(292)  评论(0)    收藏  举报

SqlCenter中的AOP实现

SqlCenter项目中实现了基于.Net Remoting 透明代理的AOP框架,取得了非常不错的效果.
本文通过验证组件的实现,简单介绍下SqlCenter中AOP 的思想和运用:)

验证组件,为了实现对类属性等的约束和判断的轻量级组件.  比如Filed实体中Name不能为空这个约束.对这个约束的判断就可以通过我们的验证组件实现. 

如果Field属性Name不能为空这个约束,用常规方法实现,则如下:

public string Name
{
get{return _name;};
set
{
     
if(value!=null)_name=value;
     
else _name =" ";
}
;
}

        这里还是一个最为简单的约束.如果更加复杂呢?同时同种约束的实体很多呢?我们的代码就需要不断的重复.有没有点小孩尿布的味道了?  是的, 我们应该有更好的方案! 
       这个时候,AOP的作用派上了用场.本文假设你对AOP基础理论和对以下知识都有了解:元数据、Attribute、.Net Remoting的代理技术.

从这里你也深入的学习到这些知识:http://www.brucezhang.com/?p=33

下面就我能理解的 "基于AOP的验证组件" 做点分析:)

先看看实现了AOP验证的代码是什么样子的:

[AspectAttribute]
public class Field : ContextBoundObject 
{
   
public string Name
   
{
    
get{return _name;}
    [NotNull]
    
set{_name = value;}
    
/*假设我们还需要他不能为"Randy"
    [NotNull,NotRandy]
     set{_name=value;}
    
*/

   }

}

是不是非常优雅?代码非常清晰,琐碎的约束性判断代码一点都没有,也就一点没有影响核心关注了:)

SqlCenter中的AOP是基于.Net Remoting 代理实现的.

上面代码告诉我们,需要自定义了一个ProxyAttribute,命名为AspectAttribute:

[AttributeUsage(AttributeTargets.Class)]
public sealed class AspectAttribute : ProxyAttribute
{
  
public override MarshalByRefObject      CreateInstance(Type serverType)
{
 MarshalByRefObject rawInstance 
=  base.CreateInstance(serverType);
 RealProxy rp 
= new AspectProxy(serverType, rawInstance);
 
return (MarshalByRefObject) rp.GetTransparentProxy();
}

}
 

通过MSDN可以知道:

将当前属性应用于需要自定义代理的类型。可以使用 ProxyAttribute 类来截获 new(在 Visual Basic 中为 New)语句,方法是:从 ProxyAttribute 派生,并将该属性置于 ContextBoundObject 的子级上。(不支持将代理属性置于 MarshalByRefObject 的子级上。)

在实现AspectAttribut中有个AspectProxy。这个是? 是的这个就是我们需要透明代理对象。它的实现:

 

class AspectProxy : System.Runtime.Remoting.Proxies.RealProxy 



//必须实现 Invoke方法 

public override IMessage Invoke(IMessage msg) 


   
//InitInterceptors(Init(){}) 

   
//OnBeginInvoke(){Begin(){}} 

   
//ExecuteMessage(){} 

   
//OnCatchException(Catch(){}) 

   
//OnEndInvoke(End(){}) 

   
//OnFinallyInvoke(Finally(){}) 

}




通过这样的实现,一个AOP组件框架就出来了! (由于是基于.Net Remoting 的实现,很多烦琐的实现都被屏蔽了,也不需要你理解IL代码了,当然认识本质还是需要的 :)

那么如何具体的实现一个组件呢?其实也很简单,只要实现了下面这个interface就算搞定了!

public interface IInterceptor
{
void Init(object target);
void Begin(IMethodCallMessage call);
void End(ref IMethodReturnMessage message);
void Catch(ref IMethodReturnMessage message);
void Finally(ref IMethodReturnMessage message);
}

就简单看看一个基于当前组件框架的验证组件的实现:

//首先,我们需要定义一个接口来规范验证组件的行为

public interface IValidator
{
           //验证过程写在Perform中

           void Perform(IValidatedElement element);
}

 //比如说NotNull,也就是上文提到的判断属性值不为Null值得组件

public class NotNullAttribute:Attribute,IValidator

{

public void Perform(IValidatedElement element)
{
if(element.Value==null)
{

//可以throw 一个异常
OnFailed(element);
}
OnSucceed(element);
}

}

是不是很简单?再来一个非空字符判断组件(有没有感觉到这个判断过程散布在系统每个角落?)

public class NotWhiteSpaceAttribute:Attribute,IValidator

{

public void Perform(IValidatedElement element)
{
if(element.Value!=null)
{
string value = element.Value as string;
if(value!=null&&value.Trim().Length ==0)
{
OnFailed(element);
}
}
OnSucceed(element);
}

}

这时你也许要问这些组件是如何发挥作用的呢?这个就要提到ValidateInterceptor这个拦截器了!因为只有拦截住对这些加了Attribute的属性的操作过程,才能有效的织入我们的验证.

下面给出它的实现:

 

public class ValidateInterceptor : IInterceptor

{

      
需要实现IInterceptor接口

//我们关注下 End方法的实现:

public void End(ref IMethodReturnMessage message)
{
    
object[] attributes = message.MethodBase.GetCustomAttributes(true);
    ValidatedMethod validatedMethod 
= new ValidatedMethod();
    validatedMethod.Init(message.MethodBase, message.ReturnValue, 
this.target);
    
foreach (object attr in attributes)
    
{
       
if (attr is IValidator)
       
{
              
//我们的验证组件Perform方法都得到了实现

             ((IValidator) attr).Perform(validatedMethod);
       }

    }

}

}



最后,回过头来看看,我们的NotNull验证组件是如何起作用的:

1.让我们的类继承于ContextBoundObject ,同时加上[AspectAttribute]

2.在我们需要不为null的属性上添加上[NotNull,NotWhiteSpaceAttribute]这个Attribute.

3.运行时:

   系统处理(get,set或方法实现上)遇到这个Attribute时,通过配置文件:

<interceptors>
<add key="ValidateInterceptor" value="SqlCenter.Validators.ValidateInterceptor, SqlCenter.Validators"/>
</interceptors>

发现处理者,并将这个消息加入到拦截器链中,再将任务转交给各自组件去处理(这里似乎也有Ioc思想和策略模式的运用) .

4.处理完成就返回,出现不符合约束要求的就做响应的处理.

上面是关于SqlCenter 中AOP思想的点点理解.

posted @ 2006-11-06 21:17 flyingchen 阅读(1220) 评论(7)  编辑 收藏 网摘 所属分类: 技术类Design Pattern架构研究Open SourceDotNet技术
  回复  引用    
#1楼 2006-11-06 22:16 | ant [未注册用户]
好文,收藏先!
  回复  引用  查看    
#2楼 2006-11-07 08:55 | 编写人生      
对于必须继承自ContextBoundObject 一直比较感冒。
我现在也在做AOP,不过框架本身并不想暴露太多的AOP部分。
  回复  引用  查看    
#3楼 [楼主]2006-11-07 09:01 | main      
@编写人生
对于单继承的C#来说,确实是个问题:(
  回复  引用  查看    
#4楼 2006-11-07 09:05 | henry      
继承ContextBoundObject来处理效率有点低。
即使不横切加入功能,对象创建或成员访问和普通对象差不是一个级别。
继承ContextBoundObject这种约束过于强制性,如果是接口还不错。
  回复  引用  查看    
#5楼 [楼主]2006-11-07 11:14 | main      
@henry
对于从ContextBoundObject继承,确实是个比较头疼的问题.
至于效率上,不知道您是否能做点分析:),还有就是不怎么理解你的第2 句话,望指教!谢谢了!
  回复  引用  查看    
#6楼 2006-11-07 11:34 | henry      
@main
效率我是这样测的(第二个其实就是效率,打少了字:()
建立两个类有同样的方法一个是继承ContextBoundObject,一个没有。
然后测试两个类的实例化和方法调用的效率,具体测试情况忘(记得两者的效率差得挺大的);如果针对小范围应用还是可以接受,在大范围中使用就是考虑了。
有时为了保证业务具备横切的功能,有可能在基类继承ContextBoundObject;这有可能导致影响的范围比较大。
  回复  引用  查看    
#7楼 [楼主]2006-11-07 12:30 | main      
@henry
感谢先,我已经看到你blog上这篇文章了:)