博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Enterprise Library深入解析与灵活应用(6):自己动手创建迷你版AOP框架
阅读量:7010 次
发布时间:2019-06-28

本文共 16195 字,大约阅读时间需要 53 分钟。

基于Enterprise Library PIAB的AOP框架已经在公司项目开发中得到广泛的使用,但是最近同事维护一个老的项目,使用到了Enterprise Library 2,所以PIAB是在Enterprise Library 3.0中推出的,所以不同直接使用。为了解决这个问题,我写了一个通过方法劫持(Method Interception)的原理,写了一个简易版的AOP框架。(如果对PIAB不是很了解的读者,可以参阅我的文章)。 Souce Code下载: 

一、如何使用?

编程方式和PIAB基本上是一样的,根据具体的需求创建相应的CallHandler,通过Custom Attribute的形式将CallHandler应用到类型或者方法上面。下面就是一个简单例子。

1: class Program
2: {
3:     static void Main(string[] args)
4:     {
5:         string userID = Guid.NewGuid().ToString();
6:         InstanceBuilder.Create
().CreateDuplicateUsers(userID, Guid.NewGuid().ToString());
7:         Console.WriteLine("Is the user whose ID is \"{0}\" has been successfully created! {1}", userID, UserUtility.UserExists(userID) ? "Yes" : "No");
8:     }
9: }
10: 
11: public class UserManager : IUserManager
12: {
13:     [ExceptionCallHandler(Ordinal = 1, MessageTemplate = "Encounter error:\nMessage:{Message}")]
14:     [TransactionScopeCallHandler(Ordinal = 2)]
15:     public void CreateDuplicateUsers(string userID, string userName)
16:     {
17:         UserUtility.CreateUser(userID, userName);
18:         UserUtility.CreateUser(userID, userName);
19:     }
20: }
21: public interface IUserManager
22: {
23:     void CreateDuplicateUsers(string userID, string userName);
24: }

在上面例子中,我创建了两个CallHandler:TransactionScopeCallHandlerExceptionCallHandler,用于进行事务和异常的处理。也就是说,我们不需要手工地进行事务的Open、Commit和Rollback的操作,也不需要通过try/catch block进行手工的异常处理。为了验证正确性,我模拟了这样的场景:数据库中有一个用户表(Users)用于存储用户帐户,每个帐户具有唯一ID,现在我通过UserManager的CreateDuplicateUsers方法插入两个具有相同ID的记录,毫无疑问,如果没有事务的处理,第一次用户添加将会成功,第二次将会失败。反之如果我们添加的TransactionScopeCallHandler能够起作用,两次操作将在同一个事务中进行,重复的记录添加将会导致事务的回退。

ExceptionCallHandler中,会对抛出的SqlException进行处理,在这我们仅仅是打印出异常相关的信息。至于具有要输出那些信息,可以通过ExceptionCallHandlerAttribute的MessageTemplate 属性定义一个输出的模板。运行程序,我们会得到这样的结果,充分证明了事务的存在,错误信息也按照我们希望的模板进行输出。

二、设计概要

同PIAB的实现原理一样,我通过自定义RealProxy实现对CallHandler的执性,从而达到方法调用劫持的目的(底层具体的实现,可以参阅我的文章)。下面的UML列出了整个框架设计的所有类型。

  • ICallHandler:所有CallHandler必须实现的接口。
  • CallHandlerBase:实现了ICallHandler的一个抽象类,是自定义CallHandler的基类。
  • HandlerAttribute:所有的CallHandler通过相应的HandlerAttribute被应用到所需的目标对象上。HandlerAttribute是一个继承自Attribute的抽象类,是自定义HandlerAttribute的基类。
  • CallHandlerPipeline:由于同一个目标方法上面可以同时应用多个CallHandler,在运行时,他们被串成一个有序的管道,依次执行。
  • InterceptingRealProxy<T>:继承自RealProxy,CallHandlerPipeline最终在Invoke方法中执行,从而实现了“方法调用劫持”。
  • InvocationContext:表示当前方法执行的上下文,Request和Reply成员表示方法的调用和返回消息。
  • InstanceBuidler:由于我们需根据InterceptingRealProxy<T>对象创建TransparentProxy,并通过TransparentProxy进行方法的调用,CallHandler才能在RealProxy中被执行。InstanceBuilder用于方便的创建TransparentProxy对象。

三、具体实现

现在我们来详细分析实现的细节。下来看看表示方法调用上下文的InvocationContext的定义。

InvocationContext 

1: public class InvocationContext
2: {
3:     public IMethodCallMessage Request
4:     { get; set; }
5: 
6:     public ReturnMessage Reply
7:     { get; set; }
8: 
9:     public IDictionary
Properties
10:     { get; set; }
11: }

Request和Reply本质上都是一个System.Runtime.Remoting.Messaging.IMessage对象。Request是IMethodCallMessage 对象,表示方法调用的消息,Reply则是ReturnMessage对象,具有可以包含具体的返回值,也可以包含抛出的异常。Properties可以供我们自由地设置一些自定义的上下文。

ICallHandler、CallHandlerBase和HandlerAttribute

ICallHandler包含四个成员,PreInvoke和PostInvoke在执行目标方法前后被先后调用,自定义CallHandler可以根据自己的具体需求实现这个两个方法。PreInvoke返回值可以通过PostInvoke的correlationState获得。Ordinal表明CallHandler在CallHandler管道的位置,他决定了应用于同一个目标方法上的多个CallHandler的执行顺序。ReturnIfError表示CallHandler在抛出异常时是否直接退出。

1: public interface ICallHandler
2: {
3:     object PreInvoke(InvocationContext context);
4: 
5:     void PostInvoke(InvocationContext context, object correlationState);
6: 
7:     int Ordinal{ get; set; }
8: 
9:     bool ReturnIfError{ get; set; }
10: }

CallHandler的抽象基类CallHandlerBase仅仅是对ICallHandler的简单实现。

1: public abstract class CallHandlerBase : ICallHandler
2: {
3:     public abstract object PreInvoke(InvocationContext context);
4: 
5:     public abstract void PostInvoke(InvocationContext context, object correlationState);
6: 
7:     public int Ordinal{ get; set; }
8: 
9:     public bool ReturnIfError { get; set; }
10: }

HandlerAttribute中定义了CreateCallHandler方法创建相应的CallHandler对象,Ordinal和ReturnIfError同上。

1: public abstract class HandlerAttribute : Attribute
2: {
3:     public abstract ICallHandler CreateCallHandler();
4: 
5:     public int Ordinal{ get; set; }
6: 
7:     public bool ReturnIfError{ get; set; }
8: }

CallHandlerPipeline

CallHandlerPipeline是CallHandler的有序集合,我们通过一个IList<ICallHandler> 对象和代码最终目标对象的创建CallHandlerPipeline。CallHandlerPipeline的核心方法是Invoke。在Invoke方法中按照CallHandler在管道中的次序先执行PreInvoke方法,然后通过反射执行目标对象的相应方法,最后逐个执行CallHandler的PostInvoke方法。

1: public class CallHandlerPipeline
2: {
3:     private object _target;
4:     private IList
_callHandlers;
5: 
6:     public CallHandlerPipeline(object target): this(new List
(), target){ }
7: 
8:     public CallHandlerPipeline(IList
callHandlers, object target)
9:     {
10:         if (target == null)
11:         {
12:             throw new ArgumentNullException("target");
13:         }
14: 
15:         if (callHandlers == null)
16:         {
17:             throw new ArgumentNullException("callHandlers");
18:         }
19: 
20:         this._target = target;
21:         this._callHandlers = callHandlers;
22:     }
23: 
24:     public void Invoke(InvocationContext context)
25:     {
26:         Stack correlationStates = new Stack();
27:         Stack
callHandlerStack = new Stack
();
28: 
29:         //Preinvoke.
30:         foreach (ICallHandler callHandler in this._callHandlers)
31:         {
32:             correlationStates.Push(callHandler.PreInvoke(context));
33:             if (context.Reply != null && context.Reply.Exception != null && callHandler.ReturnIfError)
34:             {
35:                 context.Reply = new ReturnMessage(context.Reply.Exception, context.Request);
36:                 return;
37:             }
38:             callHandlerStack.Push(callHandler);
39:         }
40: 
41:         //Invoke Target Object.
42:         object[] copiedArgs = Array.CreateInstance(typeof(object), context.Request.Args.Length) as object[];
43:         context.Request.Args.CopyTo(copiedArgs, 0);
44:         try
45:         {
46:             object returnValue = context.Request.MethodBase.Invoke(this._target, copiedArgs);
47:             context.Reply = new ReturnMessage(returnValue, copiedArgs, copiedArgs.Length, context.Request.LogicalCallContext, context.Request);
48:         }
49:         catch (Exception ex)
50:         {
51:             context.Reply = new ReturnMessage(ex, context.Request);
52:         }
53: 
54:         //PostInvoke.
55:         while (callHandlerStack.Count > 0)
56:         {
57:             ICallHandler callHandler = callHandlerStack.Pop();
58:             object correlationState = correlationStates.Pop();
59:             callHandler.PostInvoke(context, correlationState);
60:         }
61:     }
62: 
63:     public void Sort()
64:     {
65:         ICallHandler[] callHandlers = this._callHandlers.ToArray
();
66:         ICallHandler swaper = null;
67:         for (int i = 0; i < callHandlers.Length - 1; i++)
68:         {
69:             for (int j = i + 1; j < callHandlers.Length; j++)
70:             {
71:                 if (callHandlers[i].Ordinal > callHandlers[j].Ordinal)
72:                 {
73:                     swaper = callHandlers[i];
74:                     callHandlers[i] = callHandlers[j];
75:                     callHandlers[j] = swaper;
76:                 }
77:             }
78:         }
79: 
80:         this._callHandlers = callHandlers.ToList
();
81:     }
82: 
83:     public void Combine(CallHandlerPipeline pipeline)
84:     {
85:         if (pipeline == null)
86:         {
87:             throw new ArgumentNullException("pipeline");
88:         }
89: 
90:         foreach (ICallHandler callHandler in pipeline._callHandlers)
91:         {
92:             this.Add(callHandler);
93:         }
94:     }
95: 
96:     public void Combine(IList
callHandlers)
97:     {
98:         if (callHandlers == null)
99:         {
100:             throw new ArgumentNullException("callHandlers");
101:         }
102: 
103:         foreach (ICallHandler callHandler in callHandlers)
104:         {
105:             this.Add(callHandler);
106:         }
107:     }
108: 
109:     public ICallHandler Add(ICallHandler callHandler)
110:     {
111:         if (callHandler == null)
112:         {
113:             throw new ArgumentNullException("callHandler");
114:         }
115: 
116:         this._callHandlers.Add(callHandler);
117:         return callHandler;
118:     }
119: }

InterceptionRealProxy<T>

InterceptingRealProxy<T>是现在AOP的关键所在,我们通过一个IDictionary<MemberInfo, CallHandlerPipeline>和目标对象创建InterceptingRealProxy对象。在Invoke方法中,根据方法表示方法调用的IMethodCallMessage对象的MethodBase为key,从CallHandlerPipeline字典中获得基于当前方法的CallHandlerPipeline,并调用它的Invoke方法,InvocationContext的Reply即为最终的返回。

1: public class InterceptingRealProxy
: RealProxy
2: {
3:     private IDictionary
_callHandlerPipelines;
4:     public InterceptingRealProxy(object target, IDictionary
callHandlerPipelines)
5:         : base(typeof(T))
6:     {
7:         if (callHandlerPipelines == null)
8:         {
9:             throw new ArgumentNullException("callHandlerPipelines");
10:         }
11: 
12:         this._callHandlerPipelines = callHandlerPipelines;
13:     }
14:
15:     public override IMessage Invoke(IMessage msg)
16:     {
17:         InvocationContext context = new InvocationContext();
18:         context.Request = (IMethodCallMessage)msg;
19:         this._callHandlerPipelines[context.Request.MethodBase].Invoke(context);
20:         return context.Reply;
21:     }
22: }

InstanceBuidler

同PIAB通过PolicyInjection.Create()/Wrap()创建Transparent Proxy类型,InstanceBuidler也充当这样的工厂功能。InstanceBuidler的实现原理就是:通过反射获得目标类型上所有的HandlerAttribute,通过调用HandlerAttribute的CreateCallHandler创建相应的CallHandler。对于每个具体的方法,将应用在其类和方法上的所有的CallHandler组合成CallHandlerPipeline,然后以MemberInfo对象为Key将所有基于某个方法的CallHandlerPipeline构成一个CallHandlerPipeline字典。该字典,连同通过反射创建的目标对象,创建InterceptingRealProxy<T>对象。最后返回InterceptingRealProxy<T>对象的TransparentProxy对象。

1: public class InstanceBuilder
2: {
3:     public static TInterface Create
() where TObject : TInterface
4:     {
5:         TObject target = Activator.CreateInstance
();
6:         InterceptingRealProxy
realProxy = new InterceptingRealProxy
(target, CreateCallHandlerPipeline
(target));
7:         return (TInterface)realProxy.GetTransparentProxy();
8:     }
9:
10:     public static T Create
()
11:     {
12:         return Create
();
13:     }
14:
15:     public static IDictionary
CreateCallHandlerPipeline
(TObject target)
16:     {
17:         CallHandlerPipeline pipeline = new CallHandlerPipeline(target);
18:         object[] attributes = typeof(TObject).GetCustomAttributes(typeof(HandlerAttribute), true);
19:         foreach (var attribute in attributes)
20:         {
21:             HandlerAttribute handlerAttribute = attribute as HandlerAttribute;
22:             pipeline.Add(handlerAttribute.CreateCallHandler());
23:         }
24: 
25:         IDictionary
kyedCallHandlerPipelines = new Dictionary
();
26: 
27:         foreach (MethodInfo methodInfo in typeof(TObject).GetMethods())
28:         {
29:             MethodInfo declareMethodInfo = typeof(TInterfce).GetMethod(methodInfo.Name, BindingFlags.Public | BindingFlags.Instance);
30:             if (declareMethodInfo == null)
31:             {
32:                 continue;
33:             }
34:             kyedCallHandlerPipelines.Add(declareMethodInfo, new CallHandlerPipeline(target));
35:             foreach (var attribute in methodInfo.GetCustomAttributes(typeof(HandlerAttribute), true))
36:             {
37:                 HandlerAttribute handlerAttribute = attribute as HandlerAttribute;
38:                 kyedCallHandlerPipelines[declareMethodInfo].Add(handlerAttribute.CreateCallHandler());
39:             }
40:             kyedCallHandlerPipelines[declareMethodInfo].Combine(pipeline);
41:             kyedCallHandlerPipelines[declareMethodInfo].Sort();
42:         }
43: 
44:         return kyedCallHandlerPipelines;
45:     }
46: }

四、如果创建自定义CallHandler

在一开始的例子中,我们创建了两个自定义的CallHandler,一个用于进行事务处理的TranactionScopeCallHandler,另一个用于异常处理的ExceptionCallHandler。我们现在就来简单谈谈它们的实现。

TranactionScopeCallHandler

先来看看TranactionScopeCallHandler和TranactionScopeCallHandlerAttribute。我们通过TranactionScope的方式实现事务支持。在PreInvoke方法中,创建并返回TranactionScope对象,在PostInvoke中,通过correlationState参数得到该TranactionScope对象,如果没有异常(context.Reply.Exception == null),调用Complete方法提交事务。最后调用Dispose释放TranactionScope对象。(TranactionScope具有一系列的属性,在这里为了简单起见,读采用默认值)

1: public class TransactionScopeCallHandler : CallHandlerBase
2: {
3:     public override object PreInvoke(InvocationContext context)
4:     {
5:         return new TransactionScope();
6:     }
7: 
8:     public override void PostInvoke(InvocationContext context, object correlationState)
9:     {
10:         TransactionScope transactionScope = (TransactionScope)correlationState;
11:         if (context.Reply.Exception == null)
12:         {
13:             transactionScope.Complete();
14:         }
15:         transactionScope.Dispose();
16:     }
17: }
18: 
19: public class TransactionScopeCallHandlerAttribute : HandlerAttribute
20: {
21:     public override ICallHandler CreateCallHandler()
22:     {
23:         return new TransactionScopeCallHandler() { Ordinal = this.Ordinal, ReturnIfError = this.ReturnIfError };
24:     }
25: }

ExceptionCallHandler

ExceptionCallHandler的MessageTemlate和Rethrow属性分别表示最终显示的错误信息模板,和是否需要将异常抛出来。由于异常处理发生在目标方法调用之后,所以异常处理逻辑实现在PostInvoke方法中。在这里,我仅仅将通过模板组装的出错消息打印出来而已。 

1: public class ExceptionCallHandler : CallHandlerBase
2: {
3:     public string MessageTemplate{ get; set; }
4:     public bool Rethrow{ get; set; }
5: 
6:     public ExceptionCallHandler()
7:     {
8:         this.MessageTemplate = "{Message}";
9:     }
10: 
11:     public override object PreInvoke(InvocationContext context)
12:     {
13:         return null;
14:     }
15: 
16:     public override void PostInvoke(InvocationContext context, object correlationState)
17:     {
18:         if (context.Reply.Exception != null)
19:         {
20:             string message = this.MessageTemplate.Replace("{Message}", context.Reply.Exception.InnerException.Message)
21:                 .Replace("{Source}", context.Reply.Exception.InnerException.Source)
22:                 .Replace("{StackTrace}", context.Reply.Exception.InnerException.StackTrace)
23:                 .Replace("{HelpLink}", context.Reply.Exception.InnerException.HelpLink)
24:                 .Replace("{TargetSite}", context.Reply.Exception.InnerException.TargetSite.ToString());
25:             Console.WriteLine(message);
26:             if (!this.Rethrow)
27:             {
28:                 context.Reply = new ReturnMessage(null, null, 0, context.Request.LogicalCallContext, context.Request);
29:             }
30:         }
31:     }
32: }
33: 
34: public class ExceptionCallHandlerAttribute : HandlerAttribute
35: {
36: 
37:     public string MessageTemplate{ get; set; }
38: 
39:     public bool Rethrow{ get; set; }
40: 
41:     public ExceptionCallHandlerAttribute()
42:     {
43:         this.MessageTemplate = "{Message}";
44:     }
45: 
46:     public override ICallHandler CreateCallHandler()
47:     {
48:         return new ExceptionCallHandler()
49:         {
50:             Ordinal = this.Ordinal,
51:             Rethrow = this.Rethrow,
52:             MessageTemplate = this.MessageTemplate,
53:             ReturnIfError = this.ReturnIfError
54:         };
55:     }
56: }
作者:蒋金楠
微信公众账号:大内老A
微博:
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号
蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
你可能感兴趣的文章