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

点击下载源程序

原文地址:https://www.cnblogs.com/isuper/p/1240730.html