在MVC的過濾器章節中對於過濾器的使用就是AOP的一個實現了吧,時常在工做學習中遇到AOP對於它的運用能夠說是很熟練了,就是沒想過若是本身來實現的話是怎麼實現的,性子比較犟硬是沒想明白,茶不思飯不想的,主要問題就是卡在了怎麼能攔截用戶調用,若是能夠解決了這個問題AOP的實現也就引刃而解了,爲此在網上看了一些文章解決了這個問題,在這裏和你們分享一下。前端
AOP的做用就是橫切關注點,而後將分離後的關注點已面的形式來呈現,這是概念性的說法,舉個列子來講明吧。服務器
好比說有個處理過程是計算提交訂單中的貨品總額,而後想在這個過程當中執行以前記錄一下數據或者是執行一些必要的操做。ide
好比說記錄日誌,而後是選擇記錄日誌的方式,是選擇存本地文件仍是存庫,又或者是選擇了存本地文件後選擇數據的存儲介質(XML格式、文本格式、加密格式、序列化格式等等)。函數
這只是其中的一個點,好比說還有驗證等等其它一些方面的關注點。性能
圖1學習
圖2測試
從圖一、圖2中咱們就能夠看出AOP的目的,就是將日誌記錄、驗證、性能監測這些關注點從一個執行過程當中分離了出來,讓彼此再也不有關係以及和計算總額的關係。在此能夠把分離出來的關注點封裝,已「面」的形式展示出來,這樣的狀況下也使得這些「面」能夠在其它地方複用。this
1攔截的基礎實現加密
在前言中說到在.NET中實現AOP技術難點就是在攔截的那一塊,看到一些大神的文章利用remoting中的管道技術來實現信息攔截,咱們先來了解一下怎麼利用remoting來實現攔截的。spa
圖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的講解實現已經說完了,謝謝你們的閱讀。
提供源碼你們一塊兒探討,點我下載
做者:金源
出處:http://www.cnblogs.com/jin-yuan/
本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面