淺談C# AOP的簡(jiǎn)單實(shí)現(xiàn)
前言:為了弄清楚AOP,博主也是拼了。這篇打算寫寫AOP,說起AOP,其實(shí)博主接觸這個(gè)概念也才幾個(gè)月,了解后才知道,原來之前自己寫的好多代碼原理就是基于AOP的,比如MVC的過濾器Filter,它里面的異常捕捉可以通過FilterAttribute,IExceptionFilter去處理,這兩個(gè)對(duì)象的處理機(jī)制內(nèi)部原理應(yīng)該就是AOP,只不過之前沒有這個(gè)概念罷了。
一、AOP概念
老規(guī)矩,還是先看官方解釋:AOP(Aspect-Oriented Programming,面向切面的編程),它是可以通過預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)在不修改源代碼的情況下給程序動(dòng)態(tài)統(tǒng)一添加功能的一種技術(shù)。它是一種新的方法論,它是對(duì)傳統(tǒng)OOP編程的一種補(bǔ)充。OOP是關(guān)注將需求功能劃分為不同的并且相對(duì)獨(dú)立,封裝良好的類,并讓它們有著屬于自己的行為,依靠繼承和多態(tài)等來定義彼此的關(guān)系;AOP是希望能夠?qū)⑼ㄓ眯枨蠊δ軓牟幌嚓P(guān)的類當(dāng)中分離出來,能夠使得很多類共享一個(gè)行為,一旦發(fā)生變化,不必修改很多類,而只需要修改這個(gè)行為即可。AOP是使用切面(aspect)將橫切關(guān)注點(diǎn)模塊化,OOP是使用類將狀態(tài)和行為模塊化。在OOP的世界中,程序都是通過類和接口組織的,使用它們實(shí)現(xiàn)程序的核心業(yè)務(wù)邏輯是十分合適。但是對(duì)于實(shí)現(xiàn)橫切關(guān)注點(diǎn)(跨越應(yīng)用程序多個(gè)模塊的功能需求)則十分吃力,比如日志記錄,權(quán)限驗(yàn)證,異常攔截等。
博主的理解:AOP就是將公用功能提取出來,如果以后公用功能的需求發(fā)生變化,只需要改動(dòng)公用的模塊的代碼即可,多個(gè)調(diào)用的地方則不需要改動(dòng)。所謂面向切面,就是只關(guān)注通用功能,而不關(guān)注業(yè)務(wù)邏輯。實(shí)現(xiàn)方式一般是通過攔截。比如,我們隨便一個(gè)Web項(xiàng)目基本都有的權(quán)限驗(yàn)證功能,進(jìn)入每個(gè)頁面前都會(huì)校驗(yàn)當(dāng)前登錄用戶是否有權(quán)限查看該界面,我們不可能說在每個(gè)頁面的初始化方法里面都去寫這段驗(yàn)證的代碼,這個(gè)時(shí)候我們的AOP就派上用場(chǎng)了,AOP的機(jī)制是預(yù)先定義一組特性,使它具有攔截方法的功能,可以讓你在執(zhí)行方法之前和之后做你想做的業(yè)務(wù),而我們使用的時(shí)候只需要的對(duì)應(yīng)的方法或者類定義上面加上某一個(gè)特性就好了。
二、使用AOP的優(yōu)勢(shì)
博主覺得它的優(yōu)勢(shì)主要表現(xiàn)在:
1、將通用功能從業(yè)務(wù)邏輯中抽離出來,可以省略大量重復(fù)代碼,有利于代碼的操作和維護(hù)。
2、在軟件設(shè)計(jì)時(shí),抽出通用功能(切面),有利于軟件設(shè)計(jì)的模塊化,降低軟件架構(gòu)的復(fù)雜度。也就是說通用的功能都是一個(gè)單獨(dú)的模塊,在項(xiàng)目的主業(yè)務(wù)里面是看不到這些通用功能的設(shè)計(jì)代碼的。
三、AOP的簡(jiǎn)單應(yīng)用
為了說明AOP的工作原理,博主打算先從一個(gè)簡(jiǎn)單的例子開始,通過靜態(tài)攔截的方式來了解AOP是如何工作的。
1、靜態(tài)攔截
public class Order
{
public int Id { set; get; }
public string Name { set; get; }
public int Count { set; get; }
public double Price { set; get; }
public string Desc { set; get; }
}
public interface IOrderProcessor
{
void Submit(Order order);
}
public class OrderProcessor : IOrderProcessor
{
public void Submit(Order order)
{
Console.WriteLine("提交訂單");
}
}
public class OrderProcessorDecorator : IOrderProcessor
{
public IOrderProcessor OrderProcessor { get; set; }
public OrderProcessorDecorator(IOrderProcessor orderprocessor)
{
OrderProcessor = orderprocessor;
}
public void Submit(Order order)
{
PreProceed(order);
OrderProcessor.Submit(order);
PostProceed(order);
}
public void PreProceed(Order order)
{
Console.WriteLine("提交訂單前,進(jìn)行訂單數(shù)據(jù)校驗(yàn)....");
if (order.Price < 0)
{
Console.WriteLine("訂單總價(jià)有誤,請(qǐng)重新核對(duì)訂單。");
}
}
public void PostProceed(Order order)
{
Console.WriteLine("提交帶單后,進(jìn)行訂單日志記錄......");
Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "提交訂單,訂單名稱:" + order.Name + ",訂單價(jià)格:" + order.Price);
}
}
調(diào)用代碼:
static void Main(string[] args)
{
Order order = new Order() { Id = 1, Name = "lee", Count = 10, Price = 100.00, Desc = "訂單測(cè)試" };
IOrderProcessor orderprocessor = new OrderProcessorDecorator(new OrderProcessor());
orderprocessor.Submit(order);
Console.ReadLine();
}
得到結(jié)果:
上面我們模擬訂單提交的例子,在提交一個(gè)訂單前,我們需要做很多的準(zhǔn)備工作,比如數(shù)據(jù)有效性校驗(yàn)等;訂單提交完成之后,我們還需要做日志記錄等。上面的代碼很簡(jiǎn)單,沒有任何復(fù)雜的邏輯,從上面的代碼可以看出,我們通過靜態(tài)植入的方式手動(dòng)在執(zhí)行方法前和執(zhí)行方法后讓它做一些我們需要的功能。AOP的實(shí)現(xiàn)原理應(yīng)該也是如此,只不過它幫助我們做了方法攔截,幫我們省去了大量重復(fù)代碼,我們要做的僅僅是寫好攔截前和攔截后需要處理的邏輯。
2、動(dòng)態(tài)代理
了解了靜態(tài)攔截的例子,你是否對(duì)AOP有一個(gè)初步的認(rèn)識(shí)了呢。下面我們就來到底AOP該如何使用。按照?qǐng)@子里面很多牛人的說法,AOP的實(shí)現(xiàn)方式大致可以分為兩類:動(dòng)態(tài)代理和IL 編織兩種方式。博主也不打算照本宣科,分別拿Demo來說話吧。下面就以兩種方式各選一個(gè)代表框架來說明。
動(dòng)態(tài)代理方式,博主就以微軟企業(yè)庫(MS Enterprise Library)里面的PIAB(Policy Injection Application Block)框架來作說明。
首先需要下載以下幾個(gè)dll,然后添加它們的引用。
然后定義對(duì)應(yīng)的Handler
public class User
{
public string Name { set; get; }
public string PassWord { set; get; }
}
#region 1、定義特性方便使用
public class LogHandlerAttribute : HandlerAttribute
{
public string LogInfo { set; get; }
public int Order { get; set; }
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new LogHandler() { Order = this.Order, LogInfo = this.LogInfo };
}
}
#endregion
#region 2、注冊(cè)對(duì)需要的Handler攔截請(qǐng)求
public class LogHandler : ICallHandler
{
public int Order { get; set; }
public string LogInfo { set; get; }
//這個(gè)方法就是攔截的方法,可以規(guī)定在執(zhí)行方法之前和之后的攔截
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Console.WriteLine("LogInfo內(nèi)容" + LogInfo);
//0.解析參數(shù)
var arrInputs = input.Inputs;
if (arrInputs.Count > 0)
{
var oUserTest1 = arrInputs[0] as User;
}
//1.執(zhí)行方法之前的攔截
Console.WriteLine("方法執(zhí)行前攔截到了");
//2.執(zhí)行方法
var messagereturn = getNext()(input, getNext);
//3.執(zhí)行方法之后的攔截
Console.WriteLine("方法執(zhí)行后攔截到了");
return messagereturn;
}
}
#endregion
#region 3、用戶定義接口和實(shí)現(xiàn)
public interface IUserOperation
{
void Test(User oUser);
void Test2(User oUser, User oUser2);
}
//這里必須要繼承這個(gè)類MarshalByRefObject,否則報(bào)錯(cuò)
public class UserOperation : MarshalByRefObject, IUserOperation
{
private static UserOperation oUserOpertion = null;
public UserOperation()
{
//oUserOpertion = PolicyInjection.Create<UserOperation>();
}
//定義單例模式將PolicyInjection.Create<UserOperation>()產(chǎn)生的這個(gè)對(duì)象傳出去,這樣就避免了在調(diào)用處寫這些東西
public static UserOperation GetInstance()
{
if (oUserOpertion == null)
oUserOpertion = PolicyInjection.Create<UserOperation>();
return oUserOpertion;
}
//調(diào)用屬性也會(huì)攔截
public string Name { set; get; }
//[LogHandler],在方法上面加這個(gè)特性,只對(duì)此方法攔截
[LogHandler(LogInfo = "Test的日志為aaaaa")]
public void Test(User oUser)
{
Console.WriteLine("Test方法執(zhí)行了");
}
[LogHandler(LogInfo = "Test2的日志為bbbbb")]
public void Test2(User oUser, User oUser2)
{
Console.WriteLine("Test2方法執(zhí)行了");
}
}
#endregion
最后我們來看調(diào)用的代碼:
static void Main(string[] args)
{
try
{
var oUserTest1 = new User() { Name = "test2222", PassWord = "yxj" };
var oUserTest2 = new User() { Name = "test3333", PassWord = "yxj" };
var oUser = UserOperation.GetInstance();
oUser.Test(oUserTest1);
oUser.Test2(oUserTest1,oUserTest2);
}
catch (Exception ex)
{
//throw;
}
}
得到結(jié)果如下:
我們來看執(zhí)行Test()方法和Test2()方法時(shí)候的順序。
由于Test()和Test2()方法上面加了LogHander特性,這個(gè)特性里面定義了AOP的Handler,在執(zhí)行Test和Test2方法之前和之后都會(huì)進(jìn)入Invoke()方法里面。其實(shí)這就是AOP的意義所在,將切面的通用功能在統(tǒng)一的地方處理,在主要邏輯里面直接用過特性使用即可。
3、IL編織
靜態(tài)織入的方式博主打算使用PostSharp來說明,一來這個(gè)使用起來簡(jiǎn)單,二來項(xiàng)目中用過這種方式。
Postsharp從2.0版本就開始收費(fèi)了。為了說明AOP的功能,博主下載了一個(gè)免費(fèi)版本的安裝包,使用PostSharp與其它框架不太一樣的是一定要下載安裝包安裝,只引用類庫是不行的,因?yàn)樯衔恼f過,AOP框架需要為編譯器或運(yùn)行時(shí)添加擴(kuò)展。使用步驟如下:
(1)下載Postsharp安裝包,安裝。
(2)在需要使用AOP的項(xiàng)目中添加PostSharp.dll這個(gè)dll的引用。
(3)定義攔截的方法:
[Serializable]
public class TestAop : PostSharp.Aspects.OnMethodBoundaryAspect
{
//發(fā)生異常時(shí)進(jìn)入此方法
public override void OnException(MethodExecutionArgs args)
{
base.OnException(args);
}
//執(zhí)行方法前執(zhí)行此方法
public override void OnEntry(MethodExecutionArgs args)
{
base.OnEntry(args);
}
//執(zhí)行方法后執(zhí)行此方法
public override void OnExit(MethodExecutionArgs args)
{
base.OnExit(args);
}
}
注意這里的TestAop這個(gè)類必須要是可序列化的,所以要加上[Serializable]特性
(4)在需要攔截功能的地方使用。
在類上面加特性攔截,此類下面的所有的方法都會(huì)具有攔截功能。
[TestAop]public class Impc_TM_PLANT : Ifc_TM_PLANT
{
/// <summary>
/// 獲取或設(shè)置服務(wù)接口。
/// </summary>
private Ic_TM_PLANTService service { get; set; }
public IList<DTO_TM_PLANT> Find()
{
DTO_TM_PLANT otest = null;
otest.NAME_C = "test";//異常,會(huì)進(jìn)入OnException方法
return service.FindAll();
}
}
方法上面加特性攔截,只會(huì)攔截此方法。
[TestAop]
public IList<DTO_TM_PLANT> Find()
{
DTO_TM_PLANT otest = null;
otest.NAME_C = "test";
return service.FindAll();
}
有沒有感覺很簡(jiǎn)單,很強(qiáng)大,其實(shí)這一簡(jiǎn)單應(yīng)用,解決我們常見的日志、異常、權(quán)限驗(yàn)證等功能簡(jiǎn)直太小菜一碟了。當(dāng)然Postsharp可能還有許多更加高級(jí)的功能,有興趣可以深究下。
4、MVC里面的Filter
public class AOPFilterAttribute : ActionFilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
throw new System.NotImplementedException();
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
}
}
在controller里面使用該特性:
[AOPFilter]
public JsonResult GetEditModel(string strType)
{
var lstRes = new List<List<DragElementProp>>();
var lstResPage = new List<PageProperty>();
//.........todo
return Json(new { lstDataAttr = lstRes, PageAttr = lstResPage, lstJsConnections = lstJsPlumbLines }, JsonRequestBehavior.AllowGet);
}
調(diào)試可知,在執(zhí)行GetEditModel(string strType)方法之前,會(huì)先執(zhí)行OnActionExecuting()方法,GetEditModel(string strType)之后,又會(huì)執(zhí)行OnActionExecuted()方法。這在我們MVC里面權(quán)限驗(yàn)證、錯(cuò)誤頁導(dǎo)向、日志記錄等常用功能都可以方便解決。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:在WinForm應(yīng)用程序中快速實(shí)現(xiàn)多語言的處理的方法
欄 目:C#教程
下一篇:沒有了
本文標(biāo)題:淺談C# AOP的簡(jiǎn)單實(shí)現(xiàn)
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/5136.html
您可能感興趣的文章
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并打開的方法
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#停止線程的方法
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新聞效果的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已安裝軟件變化的方法
- 01-10C#實(shí)現(xiàn)多線程下載文件的方法


閱讀排行
- 1C語言 while語句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 01-11ajax實(shí)現(xiàn)頁面的局部加載
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10使用C語言求解撲克牌的順子及n個(gè)骰子
- 01-10delphi制作wav文件的方法
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 04-02jquery與jsp,用jquery
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10C#中split用法實(shí)例總結(jié)


