自己接觸NHibernate,Spring.Net,Moq這些框架有一段時間了,覺得很多很Cool的功能在這些框架中的實現都建立在動態代理這個技術之上,特來跟大家分享一下,希望大家一起討論,一起進步,我的計划是這個系列將會有四篇文章:
1..Net動態代理Castle系列(一)---初步認識
2..Net動態代理Castle系列(二)---ORM中延遲加載及實現
3..Net動態代理Castle系列(三)---在Mock框架中的運用及實現
4..Net動態代理Castle系列 (四)---在AOP中的運用及實現
今天先開始對動態代理有個初步的認識,並通過實例來讓大家能認識到Castle,並能使用Castle.
什么是動態代理?想很好的了解動態代理,就需先來了解代理的概念,代理(Proxy)這個概念在現實生活中無處不在,比如房屋中介,它就是一個代理對象,能代理行駛房東(可以理解為Target)的權力,除此之外中介可以對房東的權力添加一些附加值,攔截房東的一些行為,如收取中介費。
在設計模式中,有一種模式叫做代理模式,在GOF中這樣寫道:
為其他對象提供一種代理以控制對這個對象的訪問。
接下來,為了方便大家了解,我還是以代碼的形式來描述下中介與房東之間的故事。
首先,先來抽象房東的接口
public interface ILandlordible { void RentHouse(); }
建立一個房東的類並實現接口
public class Landlord:ILandlordible { public void RentHouse() { Console.WriteLine("房東收取租金!"); } }
房東委托中介出租,現在建立一個中介的類:
public class Intermediary:ILandlordible { private ILandlordible landlor; public Intermediary(ILandlordible landlor) { this.landlor = landlor; } private void CollectIntermediaryFees() { Console.WriteLine("中介收取服務費!"); } public void RentHouse() { landlor.RentHouse(); CollectIntermediaryFees(); } }
可以看到關於Intermediary的實現
1.首先實現一個ILandlordible的接口,因為它將具有房東出租房屋的權力
2.然后類中保存着ILandlordible的引用,因為它將被委托房東的行為
3.在RentHouse方法中,中介首先執行房東的行為,然后收取中介費。
最后,中介跟房東之間的故事就要上演啦:
class RentHouseStoryGeneralProxyVersion { static void Main(string[] args) { Console.WriteLine("房東委托中介幫其出租房屋!"); ILandlordible landlord = new Intermediary(new Landlord()); Console.WriteLine("中介成功出租房屋"); landlord.RentHouse(); } }
執行結果:
完成上面這個例子之后,想必大家對代理模式有了一定的了解,接下來將來講解動態代理模式。
動態代理,我的理解是在運行時動態地產生代理類,進而產生代理對象。結合中介這個例子,即中介類可以動態的產生,沒有必要去自己實現它。
要動態的產生一個或者攔截它的行為,在.NET平台下:
1..NET Remoting中可以利用后門進行攔截,但是必須顯示的繼承ContextBoundObject類,我覺得這個方案不是很好,因為往往具有分類學區分的類之前的關系才會采用繼承,而且會受到單繼承的限制。
2.Emit對IL直接進行操作,這個也是辦法,但是我覺得過於復雜,而且沒有必要研究到那個深度,所以我也不是很推薦。
3.在前面介紹的兩種方法之間,我選取了一個折中的難度的方法,Castle.Net 是一個開源項目,它有一個動態代理的庫能很好地解決.NET平台下動態代理的問題,也被很多流行的框架所采用,如NHibernate,Moq.
我還是來個例子,將中介的例子改編成動態代理版本:
首先根據代理,讓我們來簡單的了解下Castle中的ProxyGenerator這個類:
public class ProxyGenerator { public ProxyGenerator(); public ProxyGenerator(IProxyBuilder builder); public IProxyBuilder ProxyBuilder { get; } public T CreateClassProxy<T>(params IInterceptor[] interceptors); }
這里我摘取了ProxyGenerator的一些片段,這個類可以從名字中很容易看出它可以為我們動態地產生代理,但是仔細思考一下代理一定要有目標,如中介代理房東,所以CreateClassProxy這個泛型的方法就是指定需要代理的目標,這樣就可以我某個特定的類產生代理。但是,僅僅指定目標是不夠的,如中介它除了執行房東的職責之外,中介這個代理對象還有自己額外的邏輯,如收取服務費用。所以CreateClassProxy提供了一個可變的參數IInterceptor數組。來看看IInterceptor接口
public interface IInterceptor { void Intercept(IInvocation invocation); }
當我在調用CreateClassProxy並傳入實現了IInterceptor的對象時,所有的方法都將被IInterceptor的Intercept方法所攔截,並且Interceptor的參數IInvocation攜帶了所調用方法的所有信息,這樣我們就可以對所調用的方法進行全面的攔截,為所欲為。
那我們就開始動手完成我們動態代理版的中介與房東之間的故事吧:
繼續沿用之前的ILandlordible接口,但對於Landlord(房東)類,由於需要對它產生一個代理類,原理是需要繼承於Landlord類,對源類的方法進行重載,利用多態性,利用接口調用到重載的方法,所以對源類進行修改:
public class Landlord:ILandlordible { public virtual void RentHouse() { Console.WriteLine("房東收取租金!"); } }
修改就是將源類的方法改為虛方法,看到這里,如果用過NHibernate的朋友,是否覺得有點似曾相似,這就是為什么NHibernate的所以實體屬性需要加上Virtual關鍵字,關於原因我將在下一篇文章中跟大家一起分享。
至於Intermediary(中介)類將由動態代理產生,所以就將不再使用,現在問題的核心就在於怎么利用Castle實現代理類,並添加收取服務費的邏輯。
首先建立一個攔截器實現IInterceptor接口
public class IntermediaryIntercetor:IInterceptor { public void Intercept(IInvocation invocation) { //執行房東的邏輯 invocation.Proceed(); CollectIntermediaryFees(); } private void CollectIntermediaryFees() { Console.WriteLine("中介收取服務費!"); } }
有了這個攔截器就可以開始動態代理版的故事啦:
class RentHouseStroyDynamicProxyVersion { static void Main(string[] args) { Console.WriteLine("房東委托中介幫其出租房屋!"); Landlord proxyLandlord = CreateLandlordProxy(); Console.WriteLine("中介成功出租房屋"); proxyLandlord.RentHouse(); } private static Landlord CreateLandlordProxy() { ProxyGenerator proxyGenerator = new ProxyGenerator(); Landlord proyLandlord = proxyGenerator.CreateClassProxy<Landlord>(new IntermediaryIntercetor()); return proyLandlord; } }
執行結果: