.NET里简易实现AOP 前言
在MVC的过滤器章节中对于过滤器的使用就是AOP的一个实现了吧,时常在工作学习中遇到AOP对于它的运用可以说是很熟练了,就是没想过如果自己来实现的话是怎么实现的,性子比较犟硬是没想明白,茶不思饭不想的,主要问题就是卡在了怎么能拦截用户调用,如果可以解决了这个问题AOP的实现也就引刃而解了,为此在网上看了一些文章解决了这个问题,在这里和大家分享一下。
AOP概述
AOP的作用就是横切关注点,然后将分离后的关注点已面的形式来呈现,这是概念性的说法,举个列子来说明吧。
比如说有个处理过程是计算提交订单中的货品总额,然后想在这个过程中执行之前记录一下数据或者是执行一些必要的操作。
比如说记录日志,然后是选择记录日志的方式,是选择存本地文件还是存库,又或者是选择了存本地文件后选择数据的存储介质(XML格式、文本格式、加密格式、序列化格式等等)。
这只是其中的一个点,比如说还有验证等等其它一些方面的关注点。
图1
图2
从图1、图2中我们就可以看出AOP的目的,就是将日志记录、验证、性能监测这些关注点从一个执行过程中分离了出来,让彼此不再有关系以及和计算总额的关系。在此可以把分离出来的关注点封装,已“面”的形式展现出来,这样的情况下也使得这些“面”可以在其它地方复用。
AOP的实现
1拦截的基础实现
在前言中说到在.NET中实现AOP技术难点就是在拦截的那一块,看到一些大神的文章利用remoting中的管道技术来实现信息拦截,我们先来了解一下怎么利用remoting来实现拦截的。
图3
图3就是大概示意出了利用remoting拦截信息的一个示意图,很突兀的插了个图可能不太好理解,我们还是通过代码配合图文来粗浅的解说一下吧。
代码1-1
[AOPWriter] public class MyContextObject : ContextBoundObject { public void WriterLine(string meg) { Console.WriteLine("这是方法执行中" + meg); } }
代码1-1中的MyContextObject类型继承自上下文绑定类ContextBoundObject对象,继承过后就表示MyContextObject类型需要强制绑定上下文意思就是说它需要在一个特性环境的上下文中,对于没有继承ContextBoundObject类型的类型被称之为灵活对象,它们的诞生是在默认的当前上下文中。
这里为什么要说到这些内容呢?因为这样才能创建新的上下文,然后当前上下文对于MyContextObject的调用都是属于远程调用(在remoting里跨越了上下文边界的所有调用都应该叫远程调用,不管服务端在哪),只有这样才能利用remoting中的消息管道来进行消息拦截。
那么是在什么时候创建新的上下文的呢?在MyContextObject类型定义的上面,有个AOPWriter特性类型,我们先来看下它的定义,示例代码1-2.
代码1-2
[AttributeUsage(AttributeTargets.Class)] public class AOPWriterAttribute : Attribute, IContextAttribute { public void GetPropertiesForNewContext(IConstructionCallMessage msg) { msg.ContextProperties.Add(new AOPContextProperty()); } public bool IsContextOK(System.Runtime.Remoting.Contexts.Context ctx, IConstructionCallMessage msg) { return false; } }
在代码1-2中我们看到AOPWriterAttribute类型实现了IContextAttribute类型的接口,IContextAttribute类型的定义如下,示例代码1-3.
代码1-3
public interface IContextAttribute { // 摘要: // 在给定消息中将上下文属性返回给调用方。 // // 参数: // msg: // 将上下文属性添加到的 System.Runtime.Remoting.Activation.IConstructionCallMessage。 [SecurityCritical] void GetPropertiesForNewContext(IConstructionCallMessage msg); // // 摘要: // 返回一个布尔值,指示指定上下文是否满足上下文属性的要求。 // // 参数: // ctx: // 当前上下文属性检查所依据的上下文。 // // msg: // 构造调用,需要依据当前上下文检查其参数。 // // 返回结果: // 如果传入的上下文一切正常,则为 true;否则为 false。 [SecurityCritical] bool IsContextOK(Context ctx, IConstructionCallMessage msg); }
代码1-3中的定义同代码1-2中AOPWriterAttribute类型的实现一样,首先在上下文绑定对象进行实例化的时候系统默认的会调用IsContextOK()方法来判断当前执行实例化过程所在的上下文是否满足于上下文绑定对象的要求,这里我们在代码1-2中的实现是返回的false,意思就是当前上下文并不满足于MyContextObject类型所需要的,这是系统会去调用IContextAttribute中的GetPropertiesForNewContext()方法用于向新建上下文中添加自定义的上下文属性,也就是实现了IContextProperty接口类型的类型对象,在普通的运用中我们可以在自定义的上下文属性中设置一些逻辑操作,以便在新建上下文中使用,对于这部分的示例可以去看我的《.Net组件程序设计之上下文》篇幅。
对于代码1-2中AOPContextProperty类型的定义我们也来看一下,示例代码1-4.
代码1-4
/// <summary> /// 上下文成员属性 /// </summary> public class AOPContextProperty : IContextProperty,IContributeServerContextSink { public void Freeze(Context newContext) { } public bool IsNewContextOK(Context newCtx) { return true; } public string Name { get { return "ContextService"; } } /// <summary> /// 提供的服务 /// </summary> /// <param name="meg"></param> public void WriterMessage(string meg) { Console.WriteLine(meg); } public IMessageSink GetServerContextSink(IMessageSink nextSink) { AOPWriterSink megSink = new AOPWriterSink(nextSink); return megSink; } }
在代码1-4中上下文属性对象添加了个WriterMessage()方法,也就是上面所说的在上下文中的所有对象都可获取上下文属性中提供的逻辑操作。Name属性表示上下文属性名称,这个要是唯一的,在获取上下文属性就是根据这个Name属性值来获取的,这个下面的消息接收器示例中会有演示。
对了这里上下文属性还实现了IContributeServerContextSink类型,示例代码1-5.
代码1-5
// 摘要: // 在远程处理调用的服务器端上的上下文边界上分配侦听接收器。 [ComVisible(true)] public interface IContributeServerContextSink { // 摘要: // 将第一个接收器放入到目前为止组成的接收器链中,然后将其消息接收器连接到已经形成的链前面。 // // 参数: // nextSink: // 到目前为止组成的接收链。 // // 返回结果: // 复合接收器链。 [SecurityCritical] IMessageSink GetServerContextSink(IMessageSink nextSink); }
在上下文属性的实现中GetServerContextSink()将自定义的消息接收器添加到了新建上下文的消息接收器链的前端,这是一点非常重要我们AOP的实现主要依赖于自定义消息接收器中对于调用函数信息的拦截。
图4
对于消息接收器,是要实现IMessageSink的,代码1-6.
代码1-6
public interface IMessageSink { // 摘要: // 获取接收器链中的下一个消息接收器。 // // 返回结果: // 接收器链中的下一个消息接收器。 // // 异常: // System.Security.SecurityException: // 直接调用方通过接口引用进行调用,它没有基础结构权限。 IMessageSink NextSink { get; } [SecurityCritical] IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink); [SecurityCritical] IMessage SyncProcessMessage(IMessage msg); }
在代码的定义可以看到哟给NextSink的属性,用以链接在管道中的下个消息接收器并且已这样的形式形成消息接收器链(单向链表?职责链模式?),对于AsyncProcessMessage()方法暂且不去聊它,SyncProcessMessage()方法就可以理解为是调用的远程对象所执行的函数。来看下,示例代码1-7.
代码1-7
public class AOPWriterSink : IMessageSink { private IMessageSink m_NextSink; public AOPWriterSink(IMessageSink nextSink) { m_NextSink = nextSink; } public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) { return null; } public IMessageSink NextSink { get { return m_NextSink; } } public IMessage SyncProcessMessage(IMessage msg) { IMethodCallMessage callMessage = msg as IMethodCallMessage; if (callMessage.MethodName == "WriterLine") { Context context = Thread.CurrentContext; AOPContextProperty contextWriterService = context.GetProperty("ContextService") as AOPContextProperty; if (callMessage == null) { return null; } IMessage retMsg = null; if (contextWriterService != null) { contextWriterService.WriterMessage("方法调用之前"); } retMsg = m_NextSink.SyncProcessMessage(msg); contextWriterService.WriterMessage("方法调用之后"); return retMsg; } else { return m_NextSink.SyncProcessMessage(msg); } } }
在实例化绑定上下文对象的时候或者是调用定上下文对象的方法的时候都会调用SyncProcessMessage()方法,在SyncProcessMessage()方法中我们根据IMessage消息对象来获取当前远程对象执行方法的名称(对应代码1-1中的对象的函数名称),随之获取当前上下文属性,利用上下文属性中的逻辑操作来进行拦截后的操作。来看下测试代码结果如图5,示例代码1-8.
代码1-8
static void Main(string[] args) { MyContextObject myContextObject = new MyContextObject(); myContextObject.WriterLine("Test"); Console.ReadLine(); }
图5
2消息接收器的执行过程
这个小节我们来说明一下消息接收器的执行过程,示例还是接着上个小节的内容,需要再次定义套上面示例中的内容,代码近乎相同为了更清楚的说明所以示例出来。来看代码定义,示例代码2-1.
代码2-1
[AttributeUsage(AttributeTargets.Class)] public class AOPTestAttribute : Attribute, IContextAttribute { public void GetPropertiesForNewContext(IConstructionCallMessage msg) { msg.ContextProperties.Add(new AOPContextPropertyTest()); } public bool IsContextOK(System.Runtime.Remoting.Contexts.Context ctx, IConstructionCallMessage msg) { return false; } }
首先还是上下文绑定对象的标识特性定义,用处上节中说过了,再来看对应的上下文属性定义,示例代码2-2.
代码2-2
public class AOPContextPropertyTest : IContextProperty, IContributeServerContextSink { public void Freeze(Context newContext) { } public bool IsNewContextOK(Context newCtx) { return true; } public string Name { get { return "ContextServiceTest"; } } /// <summary> /// 提供的服务 /// </summary> /// <param name="meg"></param> public void WriterMessage(string meg) { Console.WriteLine(meg); } public IMessageSink GetServerContextSink(IMessageSink nextSink) { AOPWriterSinkTest megSink = new AOPWriterSinkTest(nextSink); return megSink; } }
这里上下文属性的定义不同于上面的内容是对于属性Name值的修改以及在设置消息接收器链的实现中重新设置了新定义的消息接收器,我们看一下新消息接收器的定义,示例代码2-3
代码2-3
public class AOPWriterSinkTest : IMessageSink { private IMessageSink m_NextSink; public AOPWriterSinkTest(IMessageSink nextSink) { m_NextSink = nextSink; } public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) { return null; } public IMessageSink NextSink { get { return m_NextSink; } } public IMessage SyncProcessMessage(IMessage msg) { IMethodCallMessage callMessage = msg as IMethodCallMessage; if (callMessage.MethodName == "WriterLine") { Context context = Thread.CurrentContext; AOPContextPropertyTest contextWriterService = context.GetProperty("ContextServiceTest") as AOPContextPropertyTest; if (callMessage == null) { return null; } IMessage retMsg = null; if (contextWriterService != null) { contextWriterService.WriterMessage("方法调用之前TEST"); } retMsg = m_NextSink.SyncProcessMessage(msg); contextWriterService.WriterMessage("方法调用之后TEST"); return retMsg; } else { return m_NextSink.SyncProcessMessage(msg); } } }
在代码2-3中SyncProcessMessage()方法中的实现也有所改变,对于获取上下文属性的参数修改为2-2中定义的属性名称了。修改代码1-1的内容,修改为代码2-4.
代码2-4
[AOPWriter] [AOPTest] public class MyContextObject : ContextBoundObject { public void WriterLine(string meg) { Console.WriteLine("这是方法执行中" + meg); } }
最后我们看一下结果图6.
图6
有的朋友可能疑问了,为什么AOPWriter在AOPTest之前而执行的结果明显的是AOPTest部分的内容先执行了,这确实跟执行关系有很大的关系,在系统首先执行的时候会先设置AOPWriter部分所对应的消息接收器到新建上下文中消息接收器链的前端,随后在设置AOPTest部分的消息接收器的时候又是重复的执行上述的操作了,所以AOPTest部分的接收器排在了前面,所以先执行了。看示意图7所表示的。
图7
看到这里想必大家已经清楚的知道了消息接收器的设置过程了,但是对消息接收器链的执行过程并不清楚,我们再横向的看一下消息接收器链执行的时候是个什么样的过程,如图8.
图8
在起初的AOPWriterSinkTest消息接收器中执行完拦截的Before操作时,会调用AOPWriterSinkTest消息接收器中的SyncProcessMessage()将消息对象往下面的消息器接收器中传递,到最后AOPWriterSink也执行完Before操作时再次向下传递的时候没有发现消息接收器了,便会调用远程对象所需执行的方法,在方法执行完毕后执行AOPWriterSink中的After操作,在AOPWriterSink的After操作执行完毕后消息也会随着起初AOPWriterSinkTest消息接收器中的SyncProcessMessage()返回,返回到了AOPWriterSinkTest消息接收器中,之后再执行它的After操作,这时的结果就如同图6中所演示的那样。
3 AOP的概念转换
在上面的小节中是讲解说明了利用remoring的技术来进行消息拦截,以及所用的词汇都是remoting中的类型词汇,在本节将会对上面的内容进行抽象,并且已AOP的概念来描述这些类型。
首先我们会对上节中实现了IContextAttribute类型的标识上下文对象进行抽象,其实也就是对AOP中关注点(切面)的抽象,来看代码定义3-1.
代码3-1
using System.Runtime.Remoting; using System.Runtime.Remoting.Contexts; using System.Runtime.Remoting.Activation; namespace FrameWork.AOP.Achieve.AOPBasics { [AttributeUsage(AttributeTargets.Class)] public abstract class AOPAttribute : Attribute, IContextAttribute { public AOPAttribute() { } public abstract AOPProperty CreateContextProperty(); public void GetPropertiesForNewContext(IConstructionCallMessage msg) { AOPProperty contextProperty = CreateContextProperty(); msg.ContextProperties.Add(contextProperty); } public bool IsContextOK(Context ctx, IConstructionCallMessage msg) { return false; } } }
代码3-1中定义和上面近似相同,只不过把需要添加的到切面中的属性(上下文属性)的实现是通过CreateContextProperty()方法来实现的,并且CreateContextProperty()方法定义为抽象的由实现类来提供具体的实现,这样做的好处可以让实现更加的灵活添置更多的可扩展点。
在对关注点(切面)进行抽象后下面要对切面中的属性进行抽象,这里我感觉它的作用在于两点,第一点是功能上的是用于连接抽象关注点和具体关注点实现的类别,第二点就是用于细化关注点,也就是第一点中说到的一个关注点中可能有N种的类别,可能这里这样说大家有点不理解什么点不点面不面的乱七八糟的概念,我这里举个例子,比如说在上面小节中我们对一个完成的执行过程进行了横切,把日志、性能监测等关注点分离出原执行过程,然而在日志这个关注点中,可能会存在普通日志、性能日志、错误日志等,对于关注点的抽象和关注点的抽象可以很好的解决这一点,也就是对关注点再次的抽象,下面我们看一下关注点属性的代码定义,示例代码3-2
using System.Runtime.Remoting.Contexts; using System.Runtime.Remoting.Activation; using System.Runtime.Remoting.Messaging; namespace FrameWork.AOP.Achieve.AOPBasics { public abstract class AOPProperty : IContextProperty, IContributeServerContextSink { public AOPProperty() { } public void Freeze(Context newContext) { } public bool IsNewContextOK(Context newCtx) { return true; } public string Name { get { return GetName(); } } protected virtual string GetName() { return "AOP"; } protected abstract IMessageSink CreateAOPAspect(IMessageSink nextSink); public IMessageSink GetServerContextSink(IMessageSink nextSink) { AOPAspect aopAspect = CreateAOPAspect(nextSink) as AOPAspect; aopAspect.AOPPropertyName = Name; return (IMessageSink)aopAspect; } } }
同样的在代码3-2中,我们将具体关注点执行类型(并不是最终执行的地方)的生成同样是放到了实现具体关注点中的实现了,并且把抽象具体关注点的Name属性值设置为GetName(),GetName()方法的定义也是可以由实现具体关注点来定义。下面我们再来看一下具体关注点执行类型的定义,示例代码3-3
代码3-3
using System.Runtime.Remoting.Contexts; using System.Runtime.Remoting.Messaging; using FrameWork.AOP.Achieve.Config; namespace FrameWork.AOP.Achieve.AOPBasics { public abstract class AOPAspect : IMessageSink { private IMessageSink _NextSink; private string _AOPPropertyName; public string AOPPropertyName { get { return _AOPPropertyName; } set { _AOPPropertyName = value; } } public AOPAspect(IMessageSink nextsink) { _NextSink = nextsink; } public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) { return null; } public IMessageSink NextSink { get { return _NextSink; } } public IMessage SyncProcessMessage(IMessage msg) { IMethodCallMessage callmsg = msg as IMethodCallMessage; if (callmsg == null) { return null; } IMessage resultMsg = null; BeforeAchieve(); resultMsg = _NextSink.SyncProcessMessage(msg); AfterAchieve(); return resultMsg; } protected virtual void BeforeAchieve() { } protected virtual void AfterAchieve () { } } }
我们在代码3-3中可以看到具体关注点执行类型的定义,并且把真正的执行包含在了具体关注点执行类型中(真正的执行是指拦截后的具体操作BeforeAchieve和AfterAchieve方法),这个是不妥的,我们会在下文中说到,并且会把它分离出去。
现在我们来看一下上面所说概念的对应关系,如图9
图9
对于关注点和具体关注点的对应关系是1对N的关系,而具体关注点和具体关注点执行类型的关系是N对N的。
现在我们来测试一下上面所示例的代码内容,会发现暴露的问题。
首先我们实现关注点,示例代码3-4.
代码3-4
using FrameWork.AOP.Achieve.AOPBasics; namespace FrameWork.AOP.Test.Log { public class AOPLog:AOPAttribute { public override AOPProperty CreateContextProperty() { return new AOPLogProperty(); } } }
之后我们再实现一个具体关注点,示例代码3-5
代码3-5
using FrameWork.AOP.Achieve.AOPBasics; using System.Runtime.Remoting.Messaging; namespace FrameWork.AOP.Test.Log { public class AOPLogProperty : AOPProperty { protected override IMessageSink CreateAOPAspect(IMessageSink nextSink) { return new AOPLogAspect(nextSink); } protected override string GetName() { return "AOPLog"; } } }
随之再实现一个具体关注点执行类型,示例代码3-6
代码3-6
using FrameWork.AOP.Achieve.AOPBasics; using System.Runtime.Remoting.Messaging; namespace FrameWork.AOP.Test.Log { public class AOPLogAspect:AOPAspect { public AOPLogAspect(IMessageSink nextSink) : base(nextSink) { } protected override void BeforeAchieve() { Console.WriteLine("MethodBefore"); } protected override void AfterAchieve() { Console.WriteLine("MethodAfter"); } } }
我们再看一下测试用例,示例代码3-7
[AOPLog] public class TestInstance : ContextBoundObject { public void TestMethod() { Console.WriteLine("MethodAction……"); } } class Program { static void Main(string[] args) { TestInstance testInstance = new TestInstance(); testInstance.TestMethod(); Console.ReadLine(); } }
最后我们看下测试结果,如图10.
图10
从这里我们可以发现一个问题,就是对于信息的拦截上面的定义是不能具体区分的,以至于在远程对象实例化的时候,连构造函数都执行了一遍拦截,这是我们不希望看到的,我们希望可以对此进行控制,第二个问题就是具体用于执行拦截的操作是不应该包含在具体关注点执行类型中的。
针对这两个问题我们要做出修改,第一通过配置信息来对信息拦截做控制,希望拦截我们所想要拦截的信息,第二将具体的执行操作从具体关注点执行类型中分离出来,通过配置信息来操作。
在做具体修改之前我们再看一下上面所有概念的关系,如图11
图11
这里的配置信息所要包含信息要有如下几点:
第一 需要有我们要拦截信息的标识
第二 所对应的自定义执行的类型
第三 拦截信息的类型
现在我们再来看一下图11经过对象化抽象过后的示意图,图12
图12
我们先从执行过程的最末端开始实现,也就是拦截后所要做的具体操作,来看示例代码3-8
代码3-8
using System.Runtime.Remoting.Messaging; namespace FrameWork.AOP.Achieve.AOPCustomAchieve { public interface IBeforeAchieve { void BeforeAchieve(IMethodCallMessage callMsg); } public interface IAfterAchieve { void AfterAchieve(IMethodReturnMessage returnMsg); } public abstract class AOPInterceptAchieve:IBeforeAchieve,IAfterAchieve { public abstract void BeforeAchieve(IMethodCallMessage callMsg); public abstract void AfterAchieve(IMethodReturnMessage returnMsg); } }
对于代码3-8不用说什么了,很明了。下面我们要对配置文件进行实现,示例代码3-9
代码3-9
using FrameWork.AOP.Achieve.AOPCustomAchieve; namespace FrameWork.AOP.Achieve.Config { public enum InterceptAchieveType { Before, After, All } public class Config { private List<string> _MethodNameList; public List<string> MethodNameList { get { return _MethodNameList; } } private InterceptAchieveType _InterceptActhieveType; public InterceptAchieveType InterceptActhieveType { get { return _InterceptActhieveType; } set { _InterceptActhieveType = value; } } private AOPInterceptAchieve _AOPInterceptAchieve; public AOPInterceptAchieve AOPInterceptAchieve { get { return _AOPInterceptAchieve; } set { _AOPInterceptAchieve = value; } } public Config() { _MethodNameList = new List<string>(); _InterceptActhieveType = InterceptAchieveType.All; } public Config(List<string> methodNameList, InterceptAchieveType interceptActhieveType) { _MethodNameList = methodNameList; _InterceptActhieveType = interceptActhieveType; } public void AddMethodName(string methodName) { if (string.IsNullOrEmpty(methodName)) { throw new ArgumentNullException("methodName"); } _MethodNameList.Add(methodName); } public void SetInterceptActhieveType(InterceptAchieveType interceptAchieveType) { _InterceptActhieveType = interceptAchieveType; } public void SetAOPInterceptAchieve(AOPInterceptAchieve aopInterceptAchieve) { if (aopInterceptAchieve == null) { throw new ArgumentNullException("aopInterceptAchieve"); } _AOPInterceptAchieve = aopInterceptAchieve; } } }
这里的配置类可以用配置文件来表示,可以表示为一个节点中所包含的信息,不过在这里我是出于简便的考虑,省去了从配置文件读取信息后转化为对象的这么一个过程。在代码3-9中定义了一个枚举类型用于设置拦截的操作类型,然后就是这个配置所要对应的拦截方法的名称集合以及具体执行操作的这么一个自定义操作抽象基类。
最后我们看一下处理配置信息类的配置信息处理引擎的定义,示例代码3-10。
代码3-10
using FrameWork.AOP.Achieve.AOPCustomAchieve; using System.Runtime.Remoting.Messaging; namespace FrameWork.AOP.Achieve.Config { public class ConfigRelationExecution { private static Dictionary<string, Config> _ConfigRelation; public static Dictionary<string, Config> ConfigRelation { get { if (_ConfigRelation == null) { _ConfigRelation = new Dictionary<string, Config>(); } return _ConfigRelation; } } public static void ActionConfigByAspectName(string aspectname, string methodname, InterceptAchieveType interceptAchieveType, IMessage msg) { if (_ConfigRelation.ContainsKey(aspectname)) { Config config = _ConfigRelation[aspectname]; if (config.MethodNameList.Contains(methodname)) { if (config.AOPInterceptAchieve != null) { if (config.InterceptActhieveType == InterceptAchieveType.All) { InterceptAchieve(interceptAchieveType, config.AOPInterceptAchieve, msg); } else if (config.InterceptActhieveType == interceptAchieveType) { InterceptAchieve(interceptAchieveType, config.AOPInterceptAchieve, msg); } } } } } private static void InterceptAchieve(InterceptAchieveType interceptAchieveType, AOPInterceptAchieve aopInterceptAchieve,IMessage msg) { if (interceptAchieveType == InterceptAchieveType.Before) { IBeforeAchieve beforeAchieve = aopInterceptAchieve as IBeforeAchieve; if (beforeAchieve != null) { beforeAchieve.BeforeAchieve((IMethodCallMessage)msg); } } else { IAfterAchieve beforeAchieve = aopInterceptAchieve as IAfterAchieve; if (beforeAchieve != null) { beforeAchieve.AfterAchieve((IMethodReturnMessage)msg); } } } } }
在代码3-10配置信息处理类中,我设置了ConfigRelation属性,这个属性的作用是用来保存具体关注点执行类型和配置信息的对应关系的,对于ActionConfigByAspectName()方法和InterceptAchieve()方法也就是一个逻辑判断的操作。根据当前具体关注点的名称来做一些判断。
现在我们修改一下代码3-3中的内容,修改为代码3-11
代码3-11
using System.Runtime.Remoting.Contexts; using System.Runtime.Remoting.Messaging; using FrameWork.AOP.Achieve.Config; namespace FrameWork.AOP.Achieve.AOPBasics { public abstract class AOPAspect : IMessageSink { private IMessageSink _NextSink; public AOPAspect(IMessageSink nextsink) { _NextSink = nextsink; } public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) { return null; } public IMessageSink NextSink { get { return _NextSink; } } private string _AOPPropertyName; public string AOPPropertyName { get { return _AOPPropertyName; } set { _AOPPropertyName = value; } } public IMessage SyncProcessMessage(IMessage msg) { IMethodCallMessage callmsg = msg as IMethodCallMessage; if (callmsg == null) { return null; } IMessage resultMsg = null; ConfigRelationExecution.ActionConfigByAspectName(_AOPPropertyName, callmsg.MethodName, InterceptAchieveType.Before, callmsg); resultMsg = _NextSink.SyncProcessMessage(msg); ConfigRelationExecution.ActionConfigByAspectName(_AOPPropertyName, callmsg.MethodName, InterceptAchieveType.After, resultMsg); return resultMsg; } } }
在这之后再修改下代码3-6的内容,删除掉两个重写基类的方法。
最后我们还是要实现一个定义拦截的操作,来看示例代码3-12
代码3-12
using System.Runtime.Remoting.Messaging; using FrameWork.AOP.Achieve; using FrameWork.AOP.Achieve.AOPCustomAchieve; namespace FrameWork.AOP.Test.Log.Achieve { public class LogWriterAchieve:AOPInterceptAchieve { public override void BeforeAchieve(IMethodCallMessage callMsg) { if (callMsg == null) { return; } Console.WriteLine(callMsg.MethodName + "—Before—" + this.GetType().Name); } public override void AfterAchieve(IMethodReturnMessage returnMsg) { if (returnMsg == null) { return; } Console.WriteLine(returnMsg.MethodName + "—After—" + this.GetType().Name); } } }
最后我们来看下测试代码,示例3-13
代码3-13
class Program { static void Main(string[] args) { Config config = new Config(); config.AddMethodName("TestMethod"); config.InterceptActhieveType = InterceptAchieveType.Before; config.AOPInterceptAchieve = new Log.Achieve.LogWriterAchieve(); ConfigRelationExecution.ConfigRelation.Add("AOPLog", config); TestInstance testInstance = new TestInstance(); testInstance.TestMethod(); Console.ReadLine(); } }
这里可以修改config.InterceptActhieveType的值依次是Before、After、All,显示的结果如下图。
还有很多测试用例就不一一的列举了,不过这里还要提一句的就是利用remoting来实现的AOP是不能满足正常运用的有个弊端,因为在远程对象方法内部调用的是远程对象内部的另一个方法时,拦截到的只能是调用的方法,被调用的则不行。
到这里对于AOP的讲解实现已经说完了,谢谢大家的阅读。
提供源码大家一起探讨,点我下载