網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
一、什么是AOP
AOP:Aspect Oriented Programming的縮寫(xiě),意為面向切面編程,通過(guò)預(yù)編譯方式和運(yùn)行期間動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。AOP是OOP思想的延續(xù)。利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開(kāi)發(fā)的效率。
為什么要學(xué)習(xí)AOP呢?
AOP的應(yīng)用場(chǎng)景非常廣泛,在一些高級(jí)工程師或者架構(gòu)師的面試過(guò)程中,頻率出現(xiàn)的比較多。
二、編程思想的發(fā)展路線
1、POP
POP:Procedure Oriented Programming的縮寫(xiě),即面向過(guò)程編程,是一種以過(guò)程為中心的編程思想。
面向過(guò)程是分析出解決問(wèn)題的步驟,然后用函數(shù)或者方法,把這些步驟一步一步的實(shí)現(xiàn),使用的時(shí)候在一個(gè)一個(gè)的一次調(diào)用函數(shù)或者方法,這就是面向過(guò)程編程。最開(kāi)始的時(shí)候都是面向過(guò)程編程。面向過(guò)程是最為實(shí)際的一種思考方式。就算是面向?qū)ο缶幊蹋锩嬉彩前忻嫦蜻^(guò)程的編程思想,因?yàn)槊嫦蜻^(guò)程是一種基礎(chǔ)的編程思考方式,它從實(shí)際出發(fā)來(lái)考慮如何實(shí)現(xiàn)需求。
POP的不足:面向過(guò)程編程,只能處理一些簡(jiǎn)單的問(wèn)題,無(wú)法處理一些復(fù)雜的問(wèn)題。如果問(wèn)題很復(fù)雜,全部以流程來(lái)思考的話,會(huì)發(fā)現(xiàn)流程很混亂,甚至流程都不能進(jìn)行下去。
2、OOP
OOP:Object Oriented Programming的縮寫(xiě),即面向?qū)ο缶幊獭?/p>
早期的計(jì)算機(jī)編程都是面向過(guò)程的,因?yàn)樵缙诘木幊潭急容^簡(jiǎn)單。但是隨著時(shí)間的發(fā)展,需求處理的問(wèn)題越來(lái)越多,問(wèn)題就會(huì)越來(lái)越復(fù)雜,這時(shí)就不能簡(jiǎn)單的使用面向過(guò)程的編程了,就出現(xiàn)了面向?qū)ο缶幊獭T谟?jì)算機(jī)里面,把所有的東西都想象成一種事物。現(xiàn)實(shí)世界中的對(duì)象都有一些屬性和行為,也就對(duì)應(yīng)計(jì)算機(jī)中的屬性和方法。
面向?qū)ο缶幊叹褪前褬?gòu)成問(wèn)題的事物分解成各個(gè)對(duì)象,建立對(duì)象的目的不是為了完成一個(gè)步驟,而是為了描述某個(gè)事物在整個(gè)解決問(wèn)題的步驟中的行為。
我們以一棟大樓為例來(lái)說(shuō)明OOP的不足。
我們把系統(tǒng)比喻成一棟樓,類或者對(duì)象是磚塊,磚塊組成了一面墻,多面墻構(gòu)成一間房間,多個(gè)房間構(gòu)成一棟樓。
這就好比一個(gè)模塊的功能是由多個(gè)類實(shí)現(xiàn),模塊又組成了某一項(xiàng)服務(wù),多個(gè)服務(wù)構(gòu)成了一個(gè)完整的系統(tǒng)。一個(gè)系統(tǒng)開(kāi)發(fā)完成并不代表就真的完成了,以后肯定會(huì)有各種需求的變更出現(xiàn),需求變更出現(xiàn)以后就要去修改代碼,代碼都在類里面,這就相當(dāng)于去修改類。如果是小范圍的修改影響還不是很大,如果是大范圍的修改,影響就比較大了。即使每次修改都很小,但是如果經(jīng)常進(jìn)行修改,影響也會(huì)很大。就會(huì)造成系統(tǒng)的不穩(wěn)定。我們得出結(jié)論:類應(yīng)該是固定的,不應(yīng)該頻繁的去修改,甚至是不允許修改。這也是為什么有那么多的設(shè)計(jì)原則和設(shè)計(jì)模式。大部分的設(shè)計(jì)模式都是為了解決這類問(wèn)題的,即在不修改類的前提下去擴(kuò)展功能。
OOP的不足:產(chǎn)生新的需求會(huì)導(dǎo)致程序代碼不斷的進(jìn)行修改,容易造成程序的不穩(wěn)定。
如果非常了解OOP的話,那么我們應(yīng)該知道,從對(duì)象的組織角度來(lái)講,分類方法都是以繼承關(guān)系為主線的,我們稱為縱向。如果只使用OOP思想的話,會(huì)帶來(lái)兩個(gè)問(wèn)題:
1、共性問(wèn)題。
2、擴(kuò)展問(wèn)題,需要對(duì)先有類進(jìn)行擴(kuò)展時(shí)就比較困難了。
OOP與POP的區(qū)別:
在對(duì)比面向過(guò)程的時(shí)候,面向?qū)ο蟮姆椒ㄊ前咽挛镒钚』癁閷?duì)象,包括屬性和方法。當(dāng)程序的規(guī)模比較小的時(shí)候,面向過(guò)程編程還是有一些優(yōu)勢(shì)的,因?yàn)檫@時(shí)候程序的流程是比較容易梳理清楚的。以早上去上班為例,過(guò)程就是起床、穿衣、刷牙洗臉、去公司。每一步都是按照順序完成的,我們只需要按照步驟去一步一步的實(shí)現(xiàn)里面的方法就行了,最后在依次調(diào)用實(shí)現(xiàn)的方法即可,這就是面向過(guò)程開(kāi)發(fā)。
如果使用面向?qū)ο缶幊蹋覀兙托枰橄蟪鰜?lái)一個(gè)員工類,該員工具有起床、穿衣、刷牙洗臉、去公司的四個(gè)方法。但是,最終要實(shí)現(xiàn)早上去上班的這個(gè)需求的話,還是要按照順序依次來(lái)調(diào)用四個(gè)方法。最開(kāi)始的時(shí)候,我們是按照面向過(guò)程的思想來(lái)思考該需求,然后在按照面向?qū)ο蟮乃枷雭?lái)抽象出幾個(gè)方法,最終要實(shí)現(xiàn)這個(gè)需求,還是要按照面向過(guò)程的順序來(lái)實(shí)現(xiàn)。
面向?qū)ο蠛兔嫦蜻^(guò)程的區(qū)別僅僅是在思考問(wèn)題方式上面的不同。最終你會(huì)發(fā)現(xiàn),在你實(shí)現(xiàn)這個(gè)需求的時(shí)候,即使使用了面向?qū)ο蟮乃枷氤橄蟪鰜?lái)了員工類,但是最后還是要使用面向過(guò)程來(lái)實(shí)現(xiàn)這個(gè)需求。
3、AOP
AOP:Aspect Oriented Programming的縮寫(xiě),即面向切面編程。是對(duì)OOP的一種補(bǔ)充,在不修改原始類的情況下,給程序動(dòng)態(tài)添加統(tǒng)一功能的一種技術(shù)。
OOP關(guān)注的是將需求功能劃分為不同的并且相對(duì)獨(dú)立、封裝良好的類,依靠繼承和多態(tài)來(lái)定義彼此的關(guān)系。AOP能夠?qū)⑼ㄓ眯枨蠊δ軓牟幌嚓P(guān)的類中分離出來(lái),很多類共享一個(gè)行為,一旦發(fā)生變化,不需要去修改很多類,只需要去修改這一個(gè)類即可。
AOP中的切面是指什么呢?切面指的是橫切關(guān)注點(diǎn)。看下面一張圖:
OOP是為了將狀態(tài)和行為進(jìn)行模塊化。上圖是一個(gè)商場(chǎng)系統(tǒng),我們使用OOP將該系統(tǒng)縱向分為訂單管理、商品管理、庫(kù)存管理模塊。在該系統(tǒng)里面,我們要進(jìn)行授權(quán)驗(yàn)證。像訂單、商品、庫(kù)存都是業(yè)務(wù)邏輯功能,但是這三個(gè)模塊都需要一些共有的功能,比如說(shuō)授權(quán)驗(yàn)證、日志記錄等。我們不可能在每個(gè)模塊里面都去寫(xiě)授權(quán)驗(yàn)證,而且授權(quán)驗(yàn)證也不屬于具體的業(yè)務(wù),它其實(shí)屬于功能性模塊,并且會(huì)橫跨多個(gè)業(yè)務(wù)模塊。可以看到這里是橫向的,這就是所謂的切面。通俗的將,AOP就是將公用的功能給提取出來(lái),如果以后這些公用的功能發(fā)生了變化,我們只需要修改這些公用功能的代碼即可,其它的地方就不需要去更改了。所謂的切面,就是只關(guān)注通用功能,而不關(guān)注業(yè)務(wù)邏輯,而且不修改原有的類。
AOP優(yōu)勢(shì):
- ?將通用功能從業(yè)務(wù)邏輯中抽離出來(lái),提高代碼復(fù)用性,有利于后期的維護(hù)和擴(kuò)展。
- 軟件設(shè)計(jì)時(shí),抽出通用功能(切面),有利于軟件設(shè)計(jì)的模塊化,降低軟件架構(gòu)的復(fù)雜度。
AOP的劣勢(shì):
- AOP的對(duì)OOP思想的一種補(bǔ)充,它無(wú)法單獨(dú)存在。如果說(shuō)單獨(dú)使用AOP去設(shè)計(jì)一套系統(tǒng)是不可能的。在設(shè)計(jì)系統(tǒng)的時(shí)候,如果系統(tǒng)比較簡(jiǎn)單,那么可以只使用POP或者OOP來(lái)設(shè)計(jì)。如果系統(tǒng)很復(fù)雜,就需要使用AOP思想。首先要使用POP來(lái)梳理整個(gè)業(yè)務(wù)流程,然后根據(jù)POP的流程,去整理類和模塊,最后在使用AOP來(lái)抽取通用功能。
AOP和OOP的區(qū)別:
- 面向目標(biāo)不同:OOP是面向名詞領(lǐng)域(抽象出來(lái)一個(gè)事物,比如學(xué)生、員工,這些都是名詞)。AOP是面向動(dòng)詞領(lǐng)域(比如鑒權(quán)、記錄日志,這些都是動(dòng)作或行為)。
- 思想結(jié)構(gòu)不同:OOP是縱向的(以繼承為主線,所以是縱向的)。AOP是橫向的。
- 注重方面不同:OOP是注重業(yè)務(wù)邏輯單元的劃分,AOP偏重業(yè)務(wù)處理過(guò)程中的某個(gè)步驟或階段。
POP、OOP、AOP三種思想是相互補(bǔ)充的。在一個(gè)系統(tǒng)的開(kāi)發(fā)過(guò)程中,這三種編程思想是不可或缺的。
三、實(shí)現(xiàn)AOP
我們?cè)谏厦嬷v解了有關(guān)AOP的一些理論知識(shí),那么如何在代碼里面實(shí)現(xiàn)呢?
實(shí)現(xiàn)AOP有兩種方式:
- 靜態(tài)代理實(shí)現(xiàn)。所謂靜態(tài)代理,就是我們自己來(lái)寫(xiě)代理對(duì)象。
- 動(dòng)態(tài)代理實(shí)現(xiàn)。所謂動(dòng)態(tài)代理,就是在程序運(yùn)行時(shí),去生成一個(gè)代理對(duì)象。
1、靜態(tài)代理
實(shí)現(xiàn)靜態(tài)代理需要使用到兩種設(shè)計(jì)模式:裝飾器模式和代理模式。
裝飾器模式:允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變這個(gè)現(xiàn)有對(duì)象的結(jié)構(gòu)。屬于結(jié)構(gòu)型設(shè)計(jì)模式,它是作為現(xiàn)有類的一種包裝。首先會(huì)創(chuàng)建一個(gè)裝飾類,用來(lái)包裝原有的類,并在保持類的完整性的前提下,提供額外的功能。看下面的例子。
我們首先創(chuàng)建一個(gè)User類:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace StaticDemo.Model { public class User { public string Name { get; set; } public string Password { get; set; } } }
接著我們創(chuàng)建一個(gè)賬號(hào)服務(wù)的接口,里面有一個(gè)方法,用來(lái)注冊(cè)一個(gè)用戶:
using StaticDemo.Model; namespace StaticDemo.Services { ////// 接口 /// public interface IAccountService { ////// 注冊(cè)用戶 /// /// void Reg(User user); } }
然后創(chuàng)建一個(gè)類來(lái)實(shí)現(xiàn)上面的接口:
using StaticDemo.Model; using System; namespace StaticDemo.Services { ////// 實(shí)現(xiàn)IAccountService接口 /// public class AccountService : IAccountService { public void Reg(User user) { // 業(yè)務(wù)代碼 之前 或者之后執(zhí)行一些其它的邏輯 Console.WriteLine($"{user.Name}注冊(cè)成功"); } } }
我們?cè)趧?chuàng)建一個(gè)裝飾器類:
using StaticDemo.Model; using StaticDemo.Services; using System; namespace StaticDemo { ////// 裝飾器類 /// public class AccountDecorator : IAccountService { private readonly IAccountService _accountService; public AccountDecorator(IAccountService accountService) { _accountService = accountService; } public void Reg(User user) { Before(); // 這里調(diào)用注冊(cè)的方法,原有類里面的邏輯不會(huì)改變 // 在邏輯前面和后面分別添加其他邏輯 _accountService.Reg(user); After(); } private void Before() { Console.WriteLine("注冊(cè)之前的邏輯"); } private void After() { Console.WriteLine("注冊(cè)之后的邏輯"); } } }
我們會(huì)發(fā)現(xiàn)裝飾器類同樣實(shí)現(xiàn)了IAccountService接口。最后我們?cè)贛ain方法里面調(diào)用:
using StaticDemo.Model; using StaticDemo.Services; using System; namespace StaticDemo { class Program { static void Main(string[] args) { // 實(shí)例化對(duì)象 IAccountService accountService = new AccountService(); // 實(shí)例化裝飾器類,并用上面的實(shí)例給構(gòu)造方法傳值 var account = new AccountDecorator(accountService); var user = new User { Name = "Rick", Password = "12345678" }; // 調(diào)用裝飾器類的注冊(cè)方法,相當(dāng)于調(diào)用實(shí)例化對(duì)象的注冊(cè)方法 account.Reg(user); Console.ReadKey(); } } }
運(yùn)行結(jié)果:
下面我們?cè)趤?lái)看看如何使用代理模式實(shí)現(xiàn)。
代理模式:即一個(gè)類代表另一個(gè)類的功能。我們會(huì)創(chuàng)建一個(gè)代理類,這個(gè)代理類和裝飾器類基本一樣。看一下代碼:
using StaticDemo.Model; using StaticDemo.Services; using System; namespace StaticDemo { ////// 代理類 /// public class ProxyAccount : IAccountService { private readonly IAccountService _accountService; ////// 構(gòu)造函數(shù)沒(méi)有參數(shù) /// 直接在里面創(chuàng)建了AccountService類 /// public ProxyAccount() { _accountService = new AccountService(); } public void Reg(User user) { before(); _accountService.Reg(user); after(); } private void before() { Console.WriteLine("代理:注冊(cè)之前的邏輯"); } private void after() { Console.WriteLine("代理:注冊(cè)之后的邏輯"); } } }
Main方法里面調(diào)用:
using StaticDemo.Model; using StaticDemo.Services; using System; namespace StaticDemo { class Program { static void Main(string[] args) { #region 裝飾器模式 //// 實(shí)例化對(duì)象 //IAccountService accountService = new AccountService(); //// 實(shí)例化裝飾器類,并用上面的實(shí)例給構(gòu)造方法傳值 //var account = new AccountDecorator(accountService); //var user = new User { Name = "Rick", Password = "12345678" }; //// 調(diào)用裝飾器類的注冊(cè)方法,相當(dāng)于調(diào)用實(shí)例化對(duì)象的注冊(cè)方法 //account.Reg(user); #endregion #region 代理模式 var account = new ProxyAccount(); var user = new User { Name = "Tom", Password = "12345678" }; account.Reg(user); #endregion Console.ReadKey(); } } }
運(yùn)行結(jié)果:
可能有的人會(huì)發(fā)現(xiàn),裝飾器類和代理類很相像,功能也一模一樣,僅僅是構(gòu)造函數(shù)不同。那么裝飾器模式和代理模式有區(qū)別嗎?有些東西,形式上看起來(lái)區(qū)別很小,但實(shí)際上他們區(qū)別很大。它們?cè)谛问缴洗_實(shí)一樣,不管是裝飾器類還是代理類,它們都要實(shí)現(xiàn)相同的接口,但是它們?cè)谶\(yùn)用的時(shí)候還是有區(qū)別的。
裝飾器模式關(guān)注于在一個(gè)對(duì)象上動(dòng)態(tài)添加方法,而代理模式關(guān)注于控制對(duì)象的訪問(wèn)。簡(jiǎn)單來(lái)說(shuō),使用代理模式,我們的代理類可以隱藏一個(gè)類的具體信息。var account = new ProxyAccount();僅看這段代碼不看源碼,不知道里面代理的是誰(shuí)。
當(dāng)使用代理模式的時(shí)候,我們常常是在代理類中去創(chuàng)建一個(gè)對(duì)象的實(shí)例:_accountService = new AccountService()。而當(dāng)我們使用裝飾器模式的時(shí)候,我們通常是將原始對(duì)象作為一個(gè)參數(shù)傳遞給裝飾器的構(gòu)造函數(shù)。簡(jiǎn)單來(lái)說(shuō),在使用裝飾器模式的時(shí)候,我們可以明確地知道裝飾的是誰(shuí),而且更重要的是,代理類里面是寫(xiě)死的,在編譯的時(shí)候就確定了關(guān)系。而裝飾器是在運(yùn)行時(shí)來(lái)確定的。
2、動(dòng)態(tài)代理
動(dòng)態(tài)代理實(shí)現(xiàn)也有兩種方式;
- 通過(guò)代碼織入的方式。例如PostSharp第三方插件。我們知道.NET程序最終會(huì)編譯成IL中間語(yǔ)言,在編譯程序的時(shí)候,PostSharp會(huì)動(dòng)態(tài)的去修改IL,在IL里面添加代碼,這就是代碼織入的方式。
- 通過(guò)反射的方式實(shí)現(xiàn)。通過(guò)反射實(shí)現(xiàn)的方法非常多,也有很多實(shí)現(xiàn)了AOP的框架,例如Unity、MVC過(guò)濾器、Autofac等。
我們先來(lái)看看如何使用PostSharp實(shí)現(xiàn)動(dòng)態(tài)代理。PostSharp是一款收費(fèi)的第三方插件。
首先新創(chuàng)建一個(gè)控制臺(tái)應(yīng)用程序,然后創(chuàng)建一個(gè)訂單業(yè)務(wù)類:
using System; namespace PostSharpDemo { ////// 訂單業(yè)務(wù)類 /// public class OrderBusiness { public void DoWork() { Console.WriteLine("執(zhí)行訂單業(yè)務(wù)"); } } }
接著在Main方法里面調(diào)用:
using System; namespace PostSharpDemo { class Program { static void Main(string[] args) { OrderBusiness order = new OrderBusiness(); // 調(diào)用方法 order.DoWork(); Console.ReadKey(); } } }
運(yùn)行結(jié)果:
這時(shí)又提出了一個(gè)新的需求,要去添加一個(gè)日志功能,記錄業(yè)務(wù)的執(zhí)行情況,按照以前的辦法,需要定義一個(gè)日志幫助類:
using System; using System.IO; namespace PostSharpDemo { public class LgoHelper { public static void RecoreLog(string message) { string strPath = AppDomain.CurrentDomain.BaseDirectory+"\\log.txt"; using(StreamWriter sw=new StreamWriter(strPath,true)) { sw.WriteLine(message); sw.Close(); } } } }
如果不使用AOP,我們就需要在記錄日志的地方實(shí)例化Loghelper對(duì)象,然后記錄日志:
using System; namespace PostSharpDemo { ////// 訂單業(yè)務(wù)類 /// public class OrderBusiness { public void DoWork() { // 記錄日志 LgoHelper.RecoreLog("執(zhí)行業(yè)務(wù)前"); Console.WriteLine("執(zhí)行訂單業(yè)務(wù)"); LgoHelper.RecoreLog("執(zhí)行業(yè)務(wù)后"); } } }
我們?cè)俅芜\(yùn)行程序,查看結(jié)果:
我們看看日志內(nèi)容:
這樣修改可以實(shí)現(xiàn)記錄日志的功能。但是上面的方法會(huì)修改原先已有的代碼,這就違反了開(kāi)閉原則。而且添加日志也不是業(yè)務(wù)需求的變動(dòng),不應(yīng)該去修改業(yè)務(wù)代碼。下面使用AOP來(lái)實(shí)現(xiàn)。首先安裝PostSharp,直接在NuGet里面搜索,然后安裝即可:
然后定義一個(gè)LogAttribute類,繼承自O(shè)nMethodBoundaryAspect。這個(gè)Aspect提供了進(jìn)入、退出函數(shù)等連接點(diǎn)方法。另外,Aspect上必須設(shè)置“[Serializable] ”,這與PostSharp內(nèi)部對(duì)Aspect的生命周期管理有關(guān):
using PostSharp.Aspects; using System; namespace PostSharpDemo { [Serializable] [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class LogAttribute: OnMethodBoundaryAspect { public string ActionName { get; set; } public override void OnEntry(MethodExecutionArgs eventArgs) { LgoHelper.RecoreLog(ActionName + "開(kāi)始執(zhí)行業(yè)務(wù)前"); } public override void OnExit(MethodExecutionArgs eventArgs) { LgoHelper.RecoreLog(ActionName + "業(yè)務(wù)執(zhí)行完成后"); } } }
然后Log特性應(yīng)用到DoWork函數(shù)上面:
using System; namespace PostSharpDemo { ////// 訂單業(yè)務(wù)類 /// public class OrderBusiness { [Log(ActionName ="DoWork")] public void DoWork() { // 記錄日志 // LgoHelper.RecoreLog("執(zhí)行業(yè)務(wù)前"); Console.WriteLine("執(zhí)行訂單業(yè)務(wù)"); // LgoHelper.RecoreLog("執(zhí)行業(yè)務(wù)后"); } } }
這樣修改以后,只需要在方法上面添加一個(gè)特性,以前記錄日志的代碼就可以注釋掉了,這樣就不會(huì)再修改業(yè)務(wù)邏輯代碼了,運(yùn)行程序:
在看看日志:
這樣就實(shí)現(xiàn)了AOP功能。
我們?cè)诳纯词褂肦emoting來(lái)實(shí)現(xiàn)動(dòng)態(tài)代理。
首先還是創(chuàng)建一個(gè)User實(shí)體類:
namespace DynamicProxy.Model { public class User { public string Name { get; set; } public string Password { get; set; } } }
然后創(chuàng)建一個(gè)接口,里面有一個(gè)注冊(cè)方法:
using DynamicProxy.Model; namespace DynamicProxy.Services { public interface IAccountService { void Reg(User user); } }
然后創(chuàng)建接口的實(shí)現(xiàn)類:
using DynamicProxy.Model; using System; namespace DynamicProxy.Services { public class AccountService : MarshalByRefObject, IAccountService { public void Reg(User user) { Console.WriteLine($"{user.Name}注冊(cè)成功"); } } }
然后創(chuàng)建一個(gè)泛型的動(dòng)態(tài)代理類:
using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Proxies; namespace DynamicProxy { public class DynamicProxy: RealProxy { private readonly T _target; // 執(zhí)行之前 public Action BeforeAction { get; set; } // 執(zhí)行之后 public Action AfterAction { get; set; } // 被代理泛型類 public DynamicProxy(T target) : base(typeof(T)) { _target = target; } // 代理類調(diào)用方法 public override IMessage Invoke(IMessage msg) { var reqMsg = msg as IMethodCallMessage; var target = _target as MarshalByRefObject; BeforeAction(); // 這里才真正去執(zhí)行代理類里面的方法 // target表示被代理的對(duì)象,reqMsg表示要執(zhí)行的方法 var result = RemotingServices.ExecuteMessage(target, reqMsg); AfterAction(); return result; } } }
我們看到,這個(gè)泛型動(dòng)態(tài)代理類里面有兩個(gè)泛型委托:BeforeAction、AfterAction。通過(guò)構(gòu)造函數(shù)把代理泛型類傳遞進(jìn)去。最后調(diào)用Invoke方法執(zhí)行代理類的方法。
最后我們還要?jiǎng)?chuàng)建一個(gè)代理工廠類,用來(lái)創(chuàng)建代理對(duì)象,通過(guò)調(diào)用動(dòng)態(tài)代理來(lái)創(chuàng)建動(dòng)態(tài)代理對(duì)象:
using System; namespace DynamicProxy { ////// 動(dòng)態(tài)代理工廠類 /// public static class ProxyFactory { public static T Create(Action before, Action after) { // 實(shí)例化被代理泛型對(duì)象 T instance = Activator.CreateInstance (); // 實(shí)例化動(dòng)態(tài)代理,創(chuàng)建動(dòng)態(tài)代理對(duì)象 var proxy = new DynamicProxy (instance) { BeforeAction = before, AfterAction = after }; // 返回透明代理對(duì)象 return (T)proxy.GetTransparentProxy(); } } }
我們最后在Main方法里面調(diào)用:
using DynamicProxy.Model; using DynamicProxy.Services; using System; namespace DynamicProxy { class Program { static void Main(string[] args) { // 調(diào)用動(dòng)態(tài)代理工廠類創(chuàng)建動(dòng)態(tài)代理對(duì)象,傳遞AccountService,并且傳遞兩個(gè)委托 var acount = ProxyFactory.Create(before:() => { Console.WriteLine("注冊(cè)之前"); }, after:() => { Console.WriteLine("注冊(cè)之后"); }); User user = new User() { Name="張三", Password="123456" }; // 調(diào)用注冊(cè)方法 acount.Reg(user); Console.ReadKey(); } } }
程序運(yùn)行結(jié)果:
這樣就利用Remoting實(shí)現(xiàn)了動(dòng)態(tài)代理。
GitHub地址:https://github.com/jxl1024/AOP
原文鏈接:https://www.cnblogs.com/dotnet261010/p/12285867.html
相關(guān)推薦
- 2022-06-12 C語(yǔ)言實(shí)例真題講解數(shù)據(jù)結(jié)構(gòu)中單向環(huán)形鏈表_C 語(yǔ)言
- 2022-05-02 利用numba讓python速度提升百倍_python
- 2022-05-08 python如何生成密碼字典_python
- 2022-08-05 Entity?Framework主從表的增刪改_C#教程
- 2022-03-06 C語(yǔ)言動(dòng)態(tài)順序表實(shí)例代碼_C 語(yǔ)言
- 2022-09-15 React組件三大屬性之state,props,refs_React
- 2022-10-21 Go錯(cuò)誤和異常CGO?fallthrough處理教程詳解_Golang
- 2022-06-11 .Net項(xiàng)目在Docker容器中開(kāi)發(fā)部署_實(shí)用技巧
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支