How to use Castle.DynamicProxy-2.1, part 1

PⅠ, Introduction

      最近对AOP比较感兴趣, 下载了一个.net下的AOP框架实现(Castle.DynamicProxy), 关于怎么使用, 网上大部分都是有关1.x版本的, 我们当然倾向于使用最新的2.1版本. 链接[1]是Castle官网推荐的使用说明, 本文主要是记录其核心和我的一些体会.

      DynamicProxy确实非常强大.

PⅡ, On The Way

      2.1, Structure & API revelation

      ---

      2.1.1

      Name: IInterceptor

      When to use: 封装实现一个具体拦截行为类

      Code:

1public interface IInterceptor
2{
3    void Intercept(IInvocation invocation);
4}

       所有的'拦截器'都必须实现IInterceptor, 因为在代码生成派生的代理类时, 其构造函数参数为IInterceptor, 依靠构造器动态'注入'具体拦截行为.

      2.1.2

      Name: IInvocation

      When: 回访调用方法属性

      Code:

 1public interface IInvocation
 2{
 3    object Proxy get; }
 4
 5    object InvocationTarget get; }
 6    
 7                             //Others
 8
 9    void Proceed();
10}

      IInvocation可以看做指向原始方法的delegate的封装, 在代码动态生成期间, 原始方法会挂钩一个IInvocation, IInvocation中的Proxy指向生成的代理类对象, Proceed()委托执行原始方法.

      2.1.3

      Name: IProxyGenerationHook

      When: 定制代码生成期间的Method/Interceptor匹配对(ShouldInterceptMethod()), 检查通知不能拦截的方法(NonVirutalMemberNotification()), 以及在完成匹配后的扫尾工作, 比如释放资源(MethodsInspected()).

      How:

      var options = new ProxyGenerationOptions(new XXXProxyGenerationHook());

      Code: 

      public interface IProxyGenerationHook
      {
          bool ShouldInterceptMethod(Type type, MethodInfo methodInfo);

          void NonVirtualMemberNotification(Type type, MemberInfo memberInfo);

          void MethodsInspected();
      }

       作为CreateProxyXXX系列方法的option参数传入, 其实现类所有的方法调用都是在代理类对象生成期间完成(对比下面的对象执行期匹配).

      2.1.4

      Name: IInterceptorSelector

      When: 定制对象执行期间的Method/Interceptor匹配对

      How:

      IInterceptorSelector selector = new FreezableInterceptorSelector();

      var options = new ProxyGenerationOptions(new XXXProxyGenerationHook()) {Selector = selector};

      Code:

1public interface IInterceptorSelector
2{
3    IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors);
4}

      作为对象执行期间的匹配原则, 只有通过IProxyGenerationHook规则(代码类生成期间)的方法才能进一步进行匹配筛选.

      2.1.5

      Name: IProxyTargetAccessor

      When: 从伪对象取得真正的代理对象(DynProxyGetTarget()), 取得所有的拦截器(GetInterceptors())

      How:

      var object = ProxyGenerator.CreateProxyXXX(...);

      IInterceptor[] interceptorList = (object as IProxyTargetAccessor).GetInterceptors();

      Code:

1public interface IProxyTargetAccessor
2{
3    object DynProxyGetTarget();
4
5    IInterceptor[] GetInterceptors();
6}

      一看就知道非常有用.

      2.1.6

      Name: IChangeProxyTarget

      When: 使用CreateInterfaceProxyWithTargetInterface创建proxy, 并需要改变代理的实际对象时.

      How:

      var changeProxyTarget = invocation as IChangeProxyTarget;

      changeProxyTarget.ChangeInvocationTarget(otherImp);

      Code:

1public interface IChangeProxyTarget
2{
3    void ChangeInvocationTarget(object target);
4}

      因为IInvocation接口包含部分属性(关键是其引用了具体的Proxy). 注意, 转换后的具体对象必须也实现了代理接口.

      2.1.7 Contrast with four types of proxy creation

      在方法重载中, 少参数方法多半是委托调用多参数方法实现, 并设置额外参数为默认. 所以我们只考虑完全参数的方法实现.

      Code:  

      1. public object CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, object[]constructorArguments, params IInterceptor[] interceptors);

      2. public object CreateInterfaceProxyWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, params IInterceptor[] interceptors);

      3. public object CreateInterfaceProxyWithTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, object target, ProxyGenerationOptions options, params IInterceptor[] interceptors);

      4. public object CreateInterfaceProxyWithTargetInterface(Type interfaceToProxy, Type[] additionalInterfacesToProxy, object target, ProxyGenerationOptions options, params IInterceptor[] interceptors);

 

      通用参数:

      Type classToProxy or interfaceToProxy, 代理对象类型.

      ProxyGenerationOptions options, 这里定义methods/interceptors匹配规则, 混合多接口(Minix) 以及其他选项.

      object[] constructorArguments, 代理对象类型的构造函数参数. 

      IInterceptor[] interceptors, 一组拦截器对象.

      特殊参数:

      @3@4, object target, 接口(interfaceToProxy)的一个具体实现对象.

      其他:

      关于CreateInterfaceProxyWithoutTarget的调用, 因为接口是不可能创建对象的, 代码会自动生成一个实现类, 但该类却不会产生任何逻辑(这是理所当然的), 所以这种情况下我们不能在拦截器的Intercept方法中调用invocation.Process().

      CreateClassProxy()与CreateInterfaceProxyWithTarget类似. 由于Proxy对象的生成本质上是继承(实现)classToProxy(interfaceToProxy)类型, 并override其中的virtual method, 达到改变行为的目的('修改代码的艺术'中的子类化并重写手法). 所以当欲代理对象类型不可继承时(sealed), 我们就不能使用CreateClassProxy()了. 这种情况下, 我们可以提取代理方法接口, 并使用CreateInterfaceProxyWithTarget()方法创建proxy. 我猜想使用CreateInterfaceproxyWithTarget(Type interfaceToProxy, ..., object target, ...)创建的proxy是实现了interfaceToProxy类型的对象, 并内部包含target成员, 各方法委托其实现.   

      使用CreateInterfaceProxyWithTargetInterface创建proxy时, 我们可以在任何可以访问IInvocation对象时转换具体的代理对象(常用做法是在拦截器中保存真实的对象引用, 并在其拦截行为中选择进行替换工作). 需要注意的是, 如果代理对象拥有多组拦截器, 在某一层拦截并替换后, 其后拦截器访问到的invocation对象引用的都是替换后的对象, 但是, 在一轮操作过后(某一个方法的所有拦截行为与其具体行为执行), 代理指向的实际对象会被重置, 即指向原始对象. 我们可以发现, 其实这是符合逻辑的(AOP). 在典型的OO设计中, 我们若进行了接口对象的替换操作, 因为不管之前还是之后的对象, 我们都会重写需要(需要仅对于基于基类的继承)的虚方法, 所以我们得到的对象都是一个个'自完全'的. 但在AOP中, 拦截的切入只能基于一种情况的约定, 可以看做一种契约形式, 如果真实的代理对象被替换后, 我们仍用原始的拦截器拦截, 那么首先就违背这个拦截器的使用约定. 如果此时需要维持逻辑上的正确, 我们就得配置一个与新对象适配的新拦截器来替换, 这等效于替换OO对象, 改写所有的虚方法.

      ---

      2.2 将介绍应用demo, 2.3 是源代码分析.

 

-----------------------------------------------

Ref:

[1], Castle dynamic proxy tutorial, http://kozmic.pl/archive/2009/04/27/castle-dynamic-proxy-tutorial.aspx

posted @ 2009-06-28 14:31  Tyrael  阅读(4180)  评论(4编辑  收藏  举报