【我們一起寫框架】C#的AOP框架

原文:【我們一起寫框架】C#的AOP框架

前言

AOP,大家都是聽過的,它是一種面向切面的設計模式。

不過AOP雖然是被稱為設計模式,但我們應該很少能看到AOP設計的框架。為什么呢?

因為,AOP單獨設計的框架幾乎是無法使用的。普遍的情況是,AOP要是和其他設計模式結合在一起使用。

所以,AOP雖然是設計模式,但我認為它更接近一種設計元素,是我們在設計框架的作料。

其實AOP的原理就是將公共的部分提取出來,這件事,即便不考慮設計模式,每個開發人員在工作時也是會做的。也就是說,在AOP設計模式被提出來之前,我們就在應用AOP的設計了。

那么,為什么還要單獨將AOP拿出來說事呢?

我認為,主要目的應該是要強化切面的重要性。因為設計框架時加入AOP的理念,確實會讓框架更加立體。

AOP的應用

AOP既然是一種作料,那么它的應用就是多種多樣的;它可以出現在任何場合的。

下面我們舉出一個例子,來說明AOP的應用。

----------------------------------------------------------------------------------------------------

我們在開發的時候,通常會有這樣的需求。

[將函數的入參和返回值記錄到日志中][入參中為負數拋出異常]

當我們面對這樣的需求時,通常會將入參和返回值全部傳到一個獨立的操作函數中,對其進行相應的操作。

這樣實現,就是AOP的理念;不過開發者處理時,稍微繁瑣了一點,因為每個函數都要處理。

為了減少這種重復操作,讓我們一起來編寫函數的切面AOP吧。

AOP框架的實現

首先,我們一起看下AOP框架應用后的效果。

在下面代碼中,可以看到,我們定義了一個AOPTest類,然后調用了他的Test方法,之后傳入了一個正數和一個負數,如果函數拋出異常,我們將輸出異常的消息。

class Program
{static void Main(string[] args){AOPTest test = new AOPTest();try{ test.Test(518);test.Test(-100);}catch(Exception ex){Console.WriteLine(ex.Message);}Console.ReadLine();}
}

接下來我們看下AOPTest類的定義。

[Kiba]
public class AOPTest : ContextBoundObject
{ public string Test(int para){Console.WriteLine(para);return "數字為:" + para;}
}

代碼如上所示,很簡單,就是輸出了入參,不過有兩個地方需要注意,該類繼承了ContextBoundObject類,并且擁有一個KIba的特性。

然后,我們看下運行結果。

從運行結果中我們看到,第一個函數正常輸出,但第二個函數拋出了異常,而且異常的Message是異常兩個漢字。

這就是我們AOP實行的效果了,我們的AOP框架對函數入參進行了判斷,如果是正數,就正常運行,如果為負數就拋出異常。

下面我們一起來看看AOP框架是如何實現這樣的效果的。

首先我們一起來看下Kiba這個特性。

[AttributeUsage(AttributeTargets.Class)]
public class KibaAttribute : ContextAttribute
{public KibaAttribute(): base("Kiba"){} public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg){ctorMsg.ContextProperties.Add(new KibaContextProperty()); }
}

代碼如上所示,很簡單很基礎的一個特性,不過它繼承了ContextAttribute類,并重寫了其下的方法GetPropertiesForNewContext。

這個方法是干什么的呢?

我們可以從函數名的直譯來理解它是干什么的,GetPropertiesForNewContext直譯過來就是創建新對象時獲取他的屬性。然后我們看到,我們重新了該方法后又為他添加了一個新的屬性。

而我們添加的這個新的屬性將截獲擁有該特性的類的函數。

【PS:該描述并不是ContextAttribute真實的運行邏輯,不過,初學時,我們可以先這樣理解,當我們更深入的理解了函數的運行機制后,自然就明白該類的意義。】

?下面我們看下KibaContextProperty類。

public class KibaContextProperty : IContextProperty, IContributeObjectSink
{public KibaContextProperty(){} public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next){return new KibaMessageSink(next);} public bool IsNewContextOK(Context newCtx){return true;}  public void Freeze(Context newCtx){}  public string Name{get { return "Kiba"; }}
}

代碼如上所示,依然很簡單,只是繼承并實現了IContextProperty和IContributeObjectSink兩個接口。

其中我們重點看下GetObjectSink方法,該方法用于截獲函數。

我們可以看到該方法的兩個參數,但我們只用到了一個IMessageSink ,并且,該方法的返回值也是IMessageSink。

所以,我們可以想到,該方法的本來面目是這樣的。

public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next)
{return next; 
} 

也就是說,IMessageSink?封裝了函數的一切內容,那么我們的AOP實現的地方也就找到了。

于是我們用KibaMessageSink類處理一下IMessageSink 。

KibaMessageSink代碼如下:

public class KibaMessageSink : IMessageSink
{private KAspec kaspec = new KAspec(); private IMessageSink nextSink; public KibaMessageSink(IMessageSink next){nextSink = next;} public IMessageSink NextSink{get{return nextSink;}} public IMessage SyncProcessMessage(IMessage msg){ IMethodCallMessage call = msg as IMethodCallMessage; if (call != null){//攔截消息,做前處理kaspec.PreExcute(call.MethodName, call.InArgs);}for (int i = 0; i < call.InArgs.Count(); i++){var para = call.InArgs[i];var type = para.GetType();string typename = type.ToString().Replace("System.Nullable`1[", "").Replace("]", "").Replace("System.", "").ToLower();if (typename == "int32"){int inparame = Convert.ToInt16(call.InArgs[i]);if (inparame < 0){throw new Exception("異常");}} }//傳遞消息給下一個接收器 IMessage retMsg = nextSink.SyncProcessMessage(call as IMessage);   IMethodReturnMessage dispose = retMsg as IMethodReturnMessage;if (dispose != null){ //調用返回時進行攔截,并進行后處理kaspec.EndExcute(dispose.MethodName, dispose.OutArgs, dispose.ReturnValue, dispose.Exception);} return retMsg;} public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink){return null;} 
}

我們重點看下SyncProcessMessage方法。

可以看到,我們在方法調用先調用了KAspec類的PreExcute方法,該方法用于把入參輸出到日志中。

接下來,我們對入參進行了判斷,如果入參是負數,我們將不執行函數,直接拋出異常。

然后我們調用KAspec類的EndExcute方法,將返回值輸出到日志中。

再然后,我們才返回IMessage,讓函數完結。

下面我們一起看下KAspec類的實現。

/// <summary>
/// 切面
/// </summary>
public class KAspec
{ #region 處理/// <summary>/// 前處理/// </summary> public void PreExcute(string MethodName, object[] InParams){Logger.Info("==================== " + MethodName + ":" + " Start====================");Logger.Info(string.Format("參數數量:{0}", InParams.Count()));for (int i = 0; i < InParams.Count(); i++){Logger.Info(string.Format("參數序號[{0}] ============    參數類型:{1}    執行類:{1}", i + 1, InParams[i])); Logger.Info("傳入參數:");string paramXMLstr = XMLSerializerToString(InParams[i], Encoding.UTF8);Logger.Info(paramXMLstr);}} /// <summary>/// 后處理/// </summary> public void EndExcute(string MethodName, object[] OutParams, object ReturnValue, Exception ex){Type myType = ReturnValue.GetType();Logger.Info(string.Format("返回值類型:{0}", myType.Name));Logger.Info("返回值:");if (myType.Name != "Void"){string resXMLstr = DataContractSerializerToString(ReturnValue, Encoding.UTF8);Logger.Info(resXMLstr);}if (OutParams.Count() > 0)//out 返回參數{Logger.Info(string.Format("out返回參數數量:{0}", OutParams.Count())); for (int i = 0; i < OutParams.Count(); i++){Logger.Info(string.Format("參數序號[{0}] == 參數值:{1}", i + 1, OutParams[i]));}}if (ex != null){Logger.Error(ex);}Logger.Info("==================== " + MethodName + ":" + " End====================");}
}

代碼如上所示,就是簡單的日志輸出。

到此,我們的AOP框架就編寫完成了;其上的代碼編寫都是為KAspec服務,因為KAspec才是切面。

也就是說,只要將特性Kiba賦予給類,那么該類的函數,就被攔截監聽,然后我們就可以KAspec切面中,做我們想做的操作了。

最后,我們再回頭看下AOPTest類。

[Kiba]
public class AOPTest : ContextBoundObject

可以看到,該類不止擁有Kiba特性,還繼承了ContextBoundObject類,該類是干什么的呢?

ContextBoundObject類是內容邊界對象,只有繼承了ContextBoundObject類的類,其類中才會駐留的Context上下文,并且會被ContextAttribute特性攔截監聽。

呃,其實,這樣解釋還是有點不太正確,不過我也沒找到更好的說明方式,如果你還理解不了,也可以去MSDN查詢下,當然,MSDN的解釋是反人類的,需要做好心理準備。

----------------------------------------------------------------------------------------------------

框架代碼已經傳到Github上了,歡迎大家下載。

Github地址:https://github.com/kiba518/KAOP

----------------------------------------------------------------------------------------------------

注:此文章為原創,歡迎轉載,請在文章頁面明顯位置給出此文鏈接!
若您覺得這篇文章還不錯,請點擊下右下角的推薦】,非常感謝!

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281668.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281668.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281668.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

新浪微博授權認證過程

為什么80%的碼農都做不了架構師&#xff1f;>>> 一、授權認證 1、請求用戶授權Token URL&#xff1a; https://api.weibo.com/oauth2/authorize HTTP請求方式:GET/POST 請求參數 必選 類型及范圍 說明 client_id true string 申請應用時分配的AppKey。 redire…

VisualStudio 使用 FastTunnel 輔助搭建遠程調試環境

有時候需要遠程調試一些用戶問題&#xff0c;期望能使用本機的 Visual Studio 開發環境&#xff0c;調試遠程的用戶的設備上的應用。這時會遇到的一個問題是如何讓本機的 Visual Studio 可以連接上遠程的用戶的設備&#xff0c;從而進行調試。本文將告訴大家如何采用 FastTunne…

深入理解null的原理

--null的原理 --oracle一直將null和空字符串’’<長度為0>同等對待<如’’ is null是true,’’null為false,如果聲明a varchar2:’’,那么a is null為true,a’’為false>--1.null的運算 --算術表達式和null 運算總為null,實際上所有的操作符除了||連接操作符外&…

阻止中文輸入法輸入拼音的時候觸發input事件

阻止中文輸入法輸入拼音的時候觸發input事件 前言 最近看element-ui源碼的時候看到el-input發現的。這個少見的事件。 compositionstart、compositionend事件&#xff08;MDN解釋) compositionstart事件觸發于一段文字的輸入之前&#xff08;類似于 keydown 事件&#xff0c;但…

Python1

python介紹python是一種解釋型的&#xff0c;面對對象的。帶有動態語義的高級程序設計語言python簡史1989年,Guido(龜叔)為ABC 語言寫的一個插件。因Monty Python的喜劇團體的原因,故給這個語言起名為python。linux也是1989年誕生的,1991年正式發布linux1.0內核;1990年, 發布py…

ncut算法matlab實現,ncut_multiscale_1_6 經典的圖像分割算法 的Matlab代碼。 238萬源代碼下載- www.pudn.com...

文件名稱: ncut_multiscale_1_6下載收藏√ [5 4 3 2 1 ]開發工具: matlab文件大小: 587 KB上傳時間: 2015-04-17下載次數: 4提 供 者: HH詳細說明&#xff1a;經典的圖像分割算法NCut的Matlab代碼。-Matlab code of classic image segmentation algorithm NCut .文件列表(…

使用.NET從零實現基于用戶角色的訪問權限控制

使用.NET從零實現基于用戶角色的訪問權限控制本文將介紹如何實現一個基于.NET RBAC 權限管理系統&#xff0c;如果您不想了解原理&#xff0c;可查看推送的另一篇文章關于Sang.AspNetCore.RoleBasedAuthorization[1] 庫是使用介紹&#xff0c;直接使用該庫即可。背景在設計系統…

數據歸一化

數據歸一化 數據的標準化是將數據按比例縮放&#xff0c;使之落入一個小的特定區間&#xff0c;一般為0到1之間。在某些比較和評價的指標處理中經常會用到&#xff0c;去除數據的單位限制&#xff0c;將其轉化為無量綱的純數值&#xff0c;便于不同單位或量級的指標能夠進行比較…

vi is failed with error E382: Cannot write, 'buftype' option is set in Linux

在linux下生成jar文件遇到了編碼問題&#xff0c;于是想vi t.jar&#xff0c;在保存是報錯&#xff1a;E382: Cannot write, buftype option is set 解決方法&#xff1a; 可以用下面的命名查看buftype的設置&#xff0c;當buftypenofile時&#xff0c;不能保存文件&#xff0c…

列表生成式的使用

輸入&#xff1a;[Hello, World, 18, Apple, None] 輸出&#xff1a;[hello, world, apple] L [Hello, World, 18, Apple, None] print([w.lower() for w in L if isinstance(w, str)])# -- coding: utf-8 -- L [Hello, World, 18, Apple, None] L2 [] L2 [w.lower() for w…

matlab 12位 顯示不出來,求助大神,為何不同機器運行MATLAB結果不同

求助&#xff1a;不同機器運行MATLAB結果不同我調用MATLAB優化工具箱的庫函數fmincon&#xff0c;使用相同的初始解(可行解)&#xff0c;對同一個問題進行局部搜索(算法為序列二次規劃&#xff0c;即SQP)&#xff0c;但在不同機器上得到的結果不同。一共有五臺機器 (為了方便&a…

.NET性能系列文章一:.NET7的性能改進

這些方法在.NET7 中變得更快照片來自 CHUTTERSNAP[1] 的 Unsplash[2]歡迎閱讀.NET 性能系列的第一章。這一系列的特點是對.NET 世界中許多不同的主題進行研究、比較性能。正如標題所說的那樣&#xff0c;本章節在于.NET7 中的性能改進。你將看到哪種方法是實現特定功能最快的方…

UVA - 10061 How many zero#39;s and how many digits ?

n!x*b^y, 當x為正整數時,最大的y就是n!末尾0的個數了, 把n,b分別拆成素因子相乘的形式: 比如, n5,b16 n5,b2^4, 非常明顯,末尾0的個數為0 10進制時,n!a*10^x b進制時,n!c*b^y 非常明顯,n!的位數就是最大的x1 這里計算我用了log,精度設置為1e-9 #include<iostream> #inclu…

丁洪波 -- 不要“ 總是拿著微不足道的成就來騙自己”

都市快報實盤大賽25期&#xff1a;于海飛/丁洪波榮獲冠亞軍 七禾網 時間&#xff1a;2010-11-02 12:47:05 來源&#xff1a;期貨中國10月30日下午&#xff0c;2010年浙商期貨實盤大賽第三季度&#xff08;都市快報實盤大賽第25期&#xff09;頒獎典禮在天科大廈浙商期貨大會議室…

面試專題(Mysql及Mongodb)

2019獨角獸企業重金招聘Python工程師標準>>> mysql面試題 1. 各個數據庫存儲引擎區別 mysql的存儲引擎是針對表進行設置的&#xff0c;一個庫的不同表可以設置不同的存儲引擎&#xff0c;mysql默認支持多種存儲引擎&#xff0c;以適用不同領域的數據庫應用需要&…

織夢網站翻頁php,dedecms織夢網站列表頁和內容頁分頁樣式

織夢分頁標簽{dede:pagelist istitem"index,pre,next,end,option,info," listsize"5"/}&#xff0c;{dede:prenext getpre/}&#xff0c;{dede:prenext getnext/}。默認樣式和使用模板css樣式布局不一樣,這時又不想重寫樣式&#xff0c;我們可以修改織夢標…

通過中間件添加用戶的Claim

本文主要介紹 Sang.AspNetCore.RoleBasedAuthorization[1] 庫如何通過中間件實現對用戶 Claim 的添加。背景前面我們介紹了通過對自定義授權策略和自定義授權處理程序的使用實現了基本的RBAC權限設計&#xff0c;將大量的用戶可訪問資源及操作的標識直接放到用戶的 JWT Token 中…

部署也是工程的一部分,也要編程(自動化)

部署和開發一樣&#xff0c;同樣面臨變化。同樣有復雜的細節。 同樣應該代碼化&#xff0c;自動化。把復雜性、思路&#xff0c;操作&#xff0c;都固化下來&#xff0c;顯式表達。 不要“雪花”式配置。 把最近看的文章摘抄一下 集句&#xff1a; 1頻繁做讓你感到痛苦的事情&a…

KDD走進阿里 數百專家聚集探討產學研一體化

6月29日&#xff0c;由阿里巴巴集團、中國中文信息學會、KDD China聯合主辦的數據挖掘前沿發展與未來論壇在杭州舉行&#xff0c;會議吸引了來自國際頂級高校和知名企業的近300名專家學者到場參會、近30000人在線觀看。論壇除了分享最新的數據挖掘領域最新科研成果及研發思路外…

zookeeper學習03 使用場景

zookeeper實際應用場景 zookeeper能夠實現哪些場景 1&#xff09;訂閱發布/配置中心 watcher機制 統一配置管理&#xff08;disconf&#xff09; 實現配置信息的集中式原理和數據的動態更新 實現配置中心有倆種模式&#xff1a;push,pull 長輪詢 zookeeper采用的是推拉相結合的…