zoukankan      html  css  js  c++  java
  • Castle AOP 系列(三):实现一个简单的调用指令路由

    在前面的章节中,我们知道了如何利用DynamicProxy程序集从类及接口中进行方法拦截。现在我们可以做一个更有趣并且更实用的操作:我们利用前面章节所讲述的知识来完成一次调用指令的路由操作,借此让我们对Castle AOP的应用层面有一个更感性的认识,因为这一章节要讲述的内容稍微有些复杂,我会试图尽量的将这些内容讲解清楚,由于写文章的水平有限,如有讲解不周的地方也请大家谅解。


    先来看看我们的第一个应用场景:

    找不到比较好的绘图工具,如果大家有的话推荐一个给我吧,我们先来解释一下这个应用场景:

    1. 我们有一个对象容器,在这个容器中包含了一系列的对象,这些对象都分别实现了某些接口。
    2. 我们假设这个对象容器是私有的,不可以被其它的程序集访问,只有一个ICommandPortal(指令门户)接口可以操作容器。
    3. 外部的程序集可以通过ICommandPortal接口来间接的访问容器里面的一些对象,ICommandPortal会解析用户的请求(Request),并依据解析后的一些信息来调用容器中相应对象的相应的方法,并返回以Response表示的调用结果。
    4. 每次想通过ICommandPortal接口调用对象容器里面的某个接口实现对象并返回值时,我们都需要:准备Request  -> 调用ICommandPortal的Invoke -> 根据返回的Response值取回调用的结果, 所以我们可以考虑通过采用DynamicProxy技术,对需要调的接口调用进行拦截,并转换出相应的Request及Response,就可以方便的完成调用过程。 

    通过上面罗列出的应用场景需求,我们可以初步确定一些类型。由于类型并不复杂,所以我们也就不逐个解讲了,我们直接看看类型的代码,同时我会在代码中加入适量的注释,大家留意查看:

    Request(用户请求)类型的定义
    1. using System;   
    2.   
    3. namespace Unit8   
    4. {   
    5.     [Serializable]   
    6.     public class Request   
    7.     {   
    8.         private string serviceKey;   
    9.         private string commandName;   
    10.         private Type[] paramTypes;   
    11.         private object[] arguments;   
    12.   
    13.         /// <summary>   
    14.         /// 容器中对象的Key值(标识)   
    15.         /// </summary>   
    16.         public string ServiceKey   
    17.         {   
    18.             get { return serviceKey; }   
    19.             set { serviceKey = value; }   
    20.         }   
    21.   
    22.         /// <summary>   
    23.         /// 需要调用的方法名称   
    24.         /// </summary>   
    25.         public string CommandName   
    26.         {   
    27.             get { return commandName; }   
    28.             set { commandName = value; }   
    29.         }   
    30.   
    31.         /// <summary>   
    32.         /// 向调用的方法传递的参数   
    33.         /// </summary>   
    34.         public object[] Arguments   
    35.         {   
    36.             get { return arguments; }   
    37.             set { arguments = value; }   
    38.         }   
    39.   
    40.         /// <summary>   
    41.         /// 调用方法的参数类型,通过这个属性来精确匹配一些有重载的函数   
    42.         /// </summary>   
    43.         public Type[] ParamTypes   
    44.         {   
    45.             get { return paramTypes; }   
    46.             set { paramTypes = value; }   
    47.         }   
    48.   
    49.         public Request()   
    50.         {   
    51.   
    52.         }   
    53.   
    54.   
    55.         public Request(string serviceKey, string commandName,    
    56.             Type[] paramTypes, object[] arguments)   
    57.         {   
    58.             this.serviceKey = serviceKey;   
    59.             this.commandName = commandName;   
    60.             this.paramTypes = paramTypes;   
    61.             this.arguments = arguments;   
    62.         }   
    63.     }   
    64. }  
    Response(响应-容器里具体对象方法调用的反回值)类型的定义
    1. using System;   
    2.   
    3. namespace Unit8   
    4. {   
    5.     [Serializable]   
    6.     public class Response   
    7.     {   
    8.         private readonly object returnValue;   
    9.   
    10.         /// <summary>   
    11.         /// 方法调用的返回值   
    12.         /// </summary>   
    13.         public object ReturnValue   
    14.         {   
    15.             get { return returnValue; }   
    16.         }   
    17.   
    18.         public Response()   
    19.         {   
    20.         }   
    21.   
    22.         public Response(object returnValue)   
    23.         {   
    24.             this.returnValue = returnValue;   
    25.         }   
    26.     }   
    27. }  

      

    ICommandPortal接口的定义
    1. namespace Unit8   
    2. {   
    3.     /// <summary>   
    4.     /// 指令调用门户   
    5.     /// </summary>   
    6.     public interface ICommandPortal   
    7.     {   
    8.         /// <summary>   
    9.         /// 根据请求的信息来调用对象容器中相应对象的相应方法,   
    10.         /// 并包装返回的值为一个Response   
    11.         /// </summary>   
    12.         Response Invoke(Request request);   
    13.     }   
    14. }  
    对象容器的实现代码,主体是一个 IDictionary<stringobject>
    1. using System.Collections.Generic;   
    2.   
    3. namespace Unit8   
    4. {   
    5.     /// <summary>   
    6.     /// 一个简单的对象容器   
    7.     /// </summary>   
    8.     public class ObjectContainer   
    9.     {   
    10.         private readonly IDictionary<stringobject> items    
    11.             = new SortedList<stringobject>();   
    12.   
    13.         public IDictionary<stringobject> Items   
    14.         {   
    15.             get { return items; }   
    16.         }   
    17.   
    18.         private ObjectContainer()   
    19.         {   
    20.   
    21.         }   
    22.   
    23.         /// <summary>   
    24.         /// 单例模式   
    25.         /// </summary>   
    26.         public static readonly ObjectContainer    
    27.             Instance = new ObjectContainer();   
    28.     }   
    29. }  
    ICommandPortal的实现代码
    1. using System;   
    2. using System.Collections.Generic;   
    3. using System.Reflection;   
    4.   
    5. namespace Unit8   
    6. {   
    7.     public class CommandPortal:ICommandPortal   
    8.     {   
    9.         public Response Invoke(Request request)   
    10.         {   
    11.             IDictionary<stringobject> Items =   
    12.                 ObjectContainer.Instance.Items;   
    13.   
    14.   
    15.                
    16.             //检测容器是否包含指定的实例对象(按ServiceKey区分)   
    17.             //如果不包含则向调用方抛回异常   
    18.             if (!Items.ContainsKey(request.ServiceKey))   
    19.                 return new Response(   
    20.                     new Exception("Invalid ServiceKey:"    
    21.                         + request.ServiceKey));   
    22.   
    23.   
    24.             object invokeObj = Items[request.ServiceKey];   
    25.   
    26.             //设置BindingFlags标志,用于指定方法的搜索条件   
    27.             //并通过反射找到相应的方法   
    28.   
    29.             const BindingFlags FindFlags =   
    30.                 BindingFlags.FlattenHierarchy | BindingFlags.Instance |    
    31.                 BindingFlags.NonPublic | BindingFlags.Public;   
    32.   
    33.   
    34.             Type invokeType = invokeObj.GetType();   
    35.   
    36.   
    37.             MethodInfo methodInfo =   
    38.                 invokeType.GetMethod(   
    39.                     request.CommandName,   
    40.                     FindFlags,   
    41.                     null,   
    42.                     request.ParamTypes,   
    43.                     null);   
    44.   
    45.   
    46.             if (methodInfo==null)   
    47.                 return new Response(   
    48.                     new Exception("Invalid MethodName:"    
    49.                         + request.CommandName));   
    50.   
    51.                
    52.             //调用找到的方法,如果调用发生异常,则返回异常的信息   
    53.             try  
    54.             {   
    55.                 return new Response(   
    56.                     methodInfo.Invoke(invokeObj,    
    57.                     request.Arguments));   
    58.             }   
    59.             catch(Exception ex)   
    60.             {   
    61.                 return new Response(ex);   
    62.             }   
    63.         }   
    64.     }   
    65. }  

    至此,我们ICommandPortal、接口实现对象的容器、Request、Response以及这几个类型之间的关系已全部作出实现,接下来,我们来看看如何产生容器内对象的Proxy。首先,我们来实现方法一个拦截器:

    方法拦截器的具体实现代码
    1. using System;   
    2. using System.Collections.Generic;   
    3. using System.Reflection;   
    4. using Castle.Core.Interceptor;   
    5.   
    6. namespace Unit8   
    7. {   
    8.     public class CommandInterceptor:IInterceptor   
    9.     {   
    10.         private readonly ICommandPortal portal;   
    11.         private readonly string serviceKey;   
    12.   
    13.         public CommandInterceptor(ICommandPortal portal,    
    14.             string serviceKey)   
    15.         {   
    16.             this.portal = portal;   
    17.             this.serviceKey = serviceKey;   
    18.         }   
    19.   
    20.         //容器内接口实现对象的Key值   
    21.         public string ServiceKey   
    22.         {   
    23.             get { return serviceKey; }   
    24.         }   
    25.   
    26.         //方法调用门户   
    27.         public ICommandPortal Portal   
    28.         {   
    29.             get { return portal; }   
    30.         }   
    31.            
    32.         public void Intercept(IInvocation invocation)   
    33.         {   
    34.             List<Type> paramTypes = new List<Type>();   
    35.   
    36.             foreach (ParameterInfo paramInfo in  
    37.                 invocation.Method.GetParameters())   
    38.                 paramTypes.Add(paramInfo.ParameterType);   
    39.   
    40.                
    41.             //生成Request对象并调用Portal的Invoke方法   
    42.             Request request = new Request(   
    43.                 ServiceKey,   
    44.                 invocation.Method.Name,   
    45.                 paramTypes.ToArray(),   
    46.                 invocation.Arguments);   
    47.   
    48.                
    49.             Response response = Portal.Invoke(request);   
    50.   
    51.   
    52.             //如果方法顺利执行,则填充返回值,否则触发异常   
    53.   
    54.             if (response.ReturnValue is Exception)   
    55.                 throw (Exception)response.ReturnValue;   
    56.                
    57.             invocation.ReturnValue = response.ReturnValue;   
    58.         }   
    59.     }   
    60. }  

    有了上面的拦截器,我们可以实现一个Proxy生成工厂,用来生成调用端接口的Proxy:

    Proxy生成工厂的代码
    1. using Castle.Core.Interceptor;   
    2. using Castle.DynamicProxy;   
    3.   
    4. namespace Unit8   
    5. {   
    6.     /// <summary>   
    7.     /// Proxy生成工厂,根据ICommandPortal,ServiceKey   
    8.     /// 产生一个动态的Proxy   
    9.     /// </summary>   
    10.     public class PortalProxyFactory   
    11.     {   
    12.         public T CreateProxy<T>(ICommandPortal commandPortal,string serviceKey)   
    13.         {   
    14.             ProxyGenerator generator = new ProxyGenerator();   
    15.             IInterceptor interceptor    
    16.                 = new CommandInterceptor(commandPortal, serviceKey);   
    17.   
    18.             T proxy = generator.CreateInterfaceProxyWithoutTarget<T>(interceptor);   
    19.   
    20.             return proxy;   
    21.         }   
    22.     }   
    23. }  

    接下来,我们来测试一下整个过程,我们来完成一个接口对象:

    一个用于测试的接口及实现
    1. namespace Unit8   
    2. {   
    3.     public interface IPerson   
    4.     {   
    5.         void SayHello(string name);   
    6.         string GetFullName(string firstName, string lastName);   
    7.     }   
    8.   
    9.     public class Person:IPerson   
    10.     {   
    11.         private readonly string innerName;   
    12.   
    13.         public Person(string innerName)   
    14.         {   
    15.             this.innerName = innerName;   
    16.         }   
    17.   
    18.         public string InnerName   
    19.         {   
    20.             get { return innerName; }   
    21.         }   
    22.   
    23.         public void SayHello(string name)   
    24.         {   
    25.             Console.WriteLine("{0} Say:Hello,{1}!", InnerName, name);   
    26.         }   
    27.   
    28.         public string GetFullName(string firstName, string lastName)   
    29.         {   
    30.             return string.Format("My name is {0} {1}.", firstName, lastName);   
    31.         }   
    32.     }   
    33. }  

    所有的工作都已就绪,我们加载整个过程来看看运行的结果:

    C#代码
    1. using System;   
    2. using System.Collections.Generic;   
    3.   
    4. namespace Unit8   
    5. {   
    6.     public class Program   
    7.     {   
    8.         private static int Main()   
    9.         {   
    10.             IDictionary<stringobject> Items =   
    11.                 ObjectContainer.Instance.Items;   
    12.   
    13.             //初始化容器里面的对象   
    14.             Items.Add("PersonA"new Person("PersonA"));   
    15.             Items.Add("PersonB"new Person("PersonB"));   
    16.   
    17.             //初始化ICommandPortal   
    18.             ICommandPortal commandPortal = new CommandPortal();   
    19.                
    20.             //初始化ProtalProxyFactory   
    21.             PortalProxyFactory proxyFactory = new PortalProxyFactory();   
    22.                
    23.             IPerson personA = proxyFactory.CreateProxy<IPerson>(commandPortal, "PersonA");   
    24.             IPerson personB = proxyFactory.CreateProxy<IPerson>(commandPortal, "PersonB");   
    25.             IPerson personC = proxyFactory.CreateProxy<IPerson>(commandPortal, "PersonC");   
    26.   
    27.   
    28.             personA.SayHello("Roger");   
    29.             Console.WriteLine(personA.GetFullName("Roger""Tong"));   
    30.   
    31.             Console.WriteLine("=========================");   
    32.   
    33.             personB.SayHello("Roger");   
    34.             Console.WriteLine(personB.GetFullName("Roger""Tong"));   
    35.   
    36.             Console.WriteLine("=========================");   
    37.   
    38.             //由于"PersonC"在容器中不存在,所以我们测试一下是否可以抛出异常
    39.             try  
    40.             {   
    41.                 personC.SayHello("Roger");   
    42.                 Console.WriteLine(personC.GetFullName("Roger""Tong"));   
    43.             }   
    44.             catch(Exception ex)   
    45.             {   
    46.                 Console.WriteLine(ex.Message);   
    47.             }   
    48.   
    49.   
    50.             Console.ReadLine();   
    51.   
    52.             return 0;   
    53.         }   
    54.     }   
    55. }  

    看一下输入的结果:

    总结:我们利用DynamicProxy产生了一个客户端调用的Proxy,并且拦截、重组(拼成Request)并转发了相应的一些方法调用的信息,并解析了返回的Response。完成了一个完整的指令路由过程。这个应用可以进行扩展的,大家可以试想一下,如果Request及Response作为字节流的方式,那么就很容易扩展为通过网络的分存式服务门户,具体的代码大家可以自己实现一下。

    本文首发地址:http://www.rogertong.cn/article.asp?id=21

    点击下载源程序

  • 相关阅读:
    我修改/收藏的CSDN知识.(asp.net JavaScript)
    哪里摔倒就在哪里躺下
    显示存储过程的名称、创建时间、修改时间
    Flash Builder 初试(二)绑定和双向绑定
    C#支持中文的格式化字符长度方法
    Flash Builder 初试(一)信息提示窗口
    Flash Builder 初试(三) 使用摄像头
    Null Object 模式
    开放封闭原则(OCP)
    面向对象设计5大准则
  • 原文地址:https://www.cnblogs.com/isuper/p/1240730.html
Copyright © 2011-2022 走看看