Enterprise Library-AOP框架
基于Enterprise Library PIAB的AOP框架已经在公司项目开发中得到广泛的使用,但是最近同事维护一个老的项目,使用到了Enterprise Library 2,所以PIAB是在Enterprise Library 3.0中推出的,所以不同直接使用。为了解决这个问题,我写了一个通过方法劫持(Method Interception)的原理,写了一个简易版的AOP框架。(如果对PIAB不是很了解的读者,可以参阅我的文章MS Enterprise Library Policy Injection Application Block 深入解析[总结篇])。 Souce Code下载:http://files.cnblogs.com/artech/Artech.SimpleAopFramework.rar
一、如何使用?
编程方式和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<UserManager, IUserManager>().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:TransactionScopeCallHandler和ExceptionCallHandler, 用于进行事务和异常的处理。也就是说,我们不需要手工地进行事务的Open、Commit和Rollback的操作,也不需要通过try/catch block进行手工的异常处理。为了验证正确性,我模拟了这样的场景:数据库中有一个用户表(Users)用于存储用户帐户,每个帐户具有唯一ID,现在 我通过UserManager的CreateDuplicateUsers方法插入两个具有相同ID的记录,毫无疑问,如果没有事务的处理,第一次用户添加将会成功,第二次将会失败。反之如果我们添加的TransactionScopeCallHandler能够起作用,两次操作将在同一个事务中进行,重复的记录添加将会导致事务的回退。
在ExceptionCallHandler中, 会对抛出的SqlException进行处理,在这我们仅仅是打印出异常相关的信息。至于具有要输出那些信息,可以通过 ExceptionCallHandlerAttribute的MessageTemplate 属性定义一个输出的模板。运行程序,我们会得到这样的结果,充分证明了事务的存在,错误信息也按照我们希望的模板进行输出。
二、设计概要
同PIAB的实现原理一样,我通过自定义RealProxy实现对CallHandler的执性,从而达到方法调用劫持的目的(底层具体的实现,可以参阅我的文章Policy Injection Application Block 设计和实现原理)。下面的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<object, object> 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<ICallHandler> _callHandlers; 5: 6: public CallHandlerPipeline(object target): this(new List<ICallHandler>(), target){ } 7: 8: public CallHandlerPipeline(IList<ICallHandler> 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<object> correlationStates = new Stack<object>(); 27: Stack<ICallHandler> callHandlerStack = new Stack<ICallHandler>(); 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<ICallHandler>(); 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<ICallHandler>(); 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<ICallHandler> 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<T> : RealProxy 2: { 3: private IDictionary<MemberInfo, CallHandlerPipeline> _callHandlerPipelines; 4: public InterceptingRealProxy(object target, IDictionary<MemberInfo, CallHandlerPipeline> 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<TObject, TInterface>() where TObject : TInterface 4: { 5: TObject target = Activator.CreateInstance<TObject>(); 6: InterceptingRealProxy<TInterface> realProxy = new InterceptingRealProxy<TInterface>(target, CreateCallHandlerPipeline<TObject, TInterface>(target)); 7: return (TInterface)realProxy.GetTransparentProxy(); 8: } 9: 10: public static T Create<T>() 11: { 12: return Create<T, T>(); 13: } 14: 15: public static IDictionary<MemberInfo, CallHandlerPipeline> CreateCallHandlerPipeline<TObject, TInterfce>(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<MemberInfo, CallHandlerPipeline> kyedCallHandlerPipelines = new Dictionary<MemberInfo, CallHandlerPipeline>(); 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: }
作者:Artech
出处:http://artech.cnblogs.com/
加支付宝好友偷能量挖...