日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

框架学习与探究之AOP--Castle DynamicProxy

發布時間:2023/12/4 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 框架学习与探究之AOP--Castle DynamicProxy 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

先說一點廢話,在此之前博主也在早期就接觸了或者看了些許AOP相關的文章,然后再去做了一些相關的實驗,但是始終沒有將AOP內化到自己的內功心法當中去,包括從概念還是應用環境,以及當前生態當中的AOP工具等等,所以這里博主還是按照以往的套路,在前人的基礎之上學習然后吸收到集成到系統當中去。


什么是AOP

還是先看官方解釋:AOP(Aspect-Oriented Programming,面向切面的編程),它是可以通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程序動態統一添加功能的一種技術。它是一種新的方法論,它是對傳統OOP編程的一種補充。OOP是關注將需求功能劃分為不同的并且相對獨立,封裝良好的類,并讓它們有著屬于自己的行為,依靠繼承和多態等來定義彼此的關系;AOP是希望能夠將通用需求功能從不相關的類當中分離出來,能夠使得很多類共享一個行為,一旦發生變化,不必修改很多類,而只需要修改這個行為即可AOP是使用切面(aspect)將橫切關注點模塊化,OOP是使用類將狀態和行為模塊化。在OOP的世界中,程序都是通過類和接口組織的,使用它們實現程序的核心業務邏輯是十分合適。但是對于實現橫切關注點(跨越應用程序多個模塊的功能需求)則十分吃力,比如日志記錄,權限驗證,異常攔截等。這里可以看到當我們在應用程序中使用AOP來編程的話,是大大的節約我的代碼量而且做到了職責依然分明,所以有這么多好處的情況下,我們就應該勇敢的嘗試來構建我的應用程序...


.NET當中的AOP

根據上文關于AOP的兩種方式來實現了面向切面編程,而在.NET領域當中有些比較出名的,例如在動態代理方面就是castle.core(原名:castle dynamic proxy)通過Reflect.Emit的方式在原始基礎上面實現的動態代理方式,加上緩沖機制提升性能,該項目很好的屏蔽了底層使用emit+opcode的方式帶來的復雜性和難度,使用友好的API調用方式提供外部程序訪問(castle.coree nuget地址,小吐槽:.net core已經2.0這么久了,castle家族雖然也還在努力,但是依然還沒支持.net standrad2.0,可能是因為castle.core現在打包了不止dynamicproxy一個包的原因,需要一些時間,不過在@Lemon努力下率先提供了基于.net core的aop框架,這里博主也是對此框架目前也是淺嘗輒止的狀態還沒有應用到項目中,所以不好對比評價哈,哈哈!),所以基于我們當前的項目而已動態中的castle不失為我們的一個好的選擇;然后在.net靜態編織方面當屬postsharp首當其沖了,當然這玩意兒是收費不過你可以使用express免費版但是和企業版對dll的修改差異就不得而知了,當初博主實驗安裝的企業版也是在免費期內使用后面做了一些小手腳才得以繼續使用,當然這里不推薦破解使用了,有錢的主兒還是支持正版吧注意使用postsharp需要vs安裝插件(需要插件支持才可以編譯后期對dll動手腳)以及nuget package支持,所以團隊里面如果使用需要大家統一安裝,還有就是目前許多都是基于Jenkins來做持續集成的,所以還得注意使用了postsharp的項目編譯問題(使用了postsharp如果沒有插件支持會報錯)!!當然我這里的指出相對比較計較的性能問題,靜態編織是比動態代理來的更快,因為dll在編譯之后都已經形成了AOP之勢,動態代理畢竟是運行時才得以去構建各種代理對象不過還好有緩沖,所以相對一般情況動態代理足以滿足我們的需求,還是那個句話:在已經解決顯而易見的瓶頸之后,才考慮一些錙銖必較的性能缺失!!!最后.NET周邊其他AOP框架或者產品不管是動態還是靜態,大家可自行搜索是有的,不過使用率以及可用性、穩定性、性能等等就不好說了!


Castle DynamicProxy文檔帶來了什么

首先Castle DynamicProxy是一個在運行時即時生成輕量級.NET代理的庫,然后除了介紹AOP使用場景就是當前一些流行項目對于castle.core的依賴,側面也反應除了此AOP的地位,好吧,233333,官方文檔是要捧一下自己的。我們會發現?moq(mock的實現)、NHibernate(延遲加載virtual+getter攔截實現等)等都依賴了castle.core,說明了上面的框架功能提供肯定是需要動態代理的支撐的,然后根據官方說明在版本2.5之后原先獨立的castle.dynamicproxy.dll就合并了,官方高能如果您有一個長時間運行的進程(網站,Windows服務等),并且必須創建許多動態代理,那么您應該確保重用相同的ProxyGenerator實例。如果沒有,請注意,您將繞過緩存機制,副作用是CPU使用率高,內存消耗不斷增加。

這里引用官方對于catle dynamicproxy的工作原理及流程的理解,顯示給出執行流程圖:

我們這里外部的藍色框就是代理區域,黃色箭頭將會層層進入各級代理對象,接著代理執行PreAction邏輯,然后調用invocation.Proceed()(每個代理只能調用一次不然出爆發異常)進入下一個代理或者原始對象邏輯,就這樣一直進入到最下層也就是被代理的對象,執行原始邏輯之后,再按照層層代理執行PostAction邏輯彈出也就是綠色箭頭所表達的意思,就是一條完成的傳遞鏈就形成了多級代理模式。注意點:每一層代理對象都可以拿到目標的對象也就是被代理對象IInvocation,該接口包含了一些重要屬性和方法,例如:invocation.MethodInfo、invocation.ResultValue 等對象,我們接下來對API實驗詳細看看這些對象的真實面目,至此根據官方文檔就這樣沒了,還有些stackoverflow的參考代碼,我們同時翻看園中其他使用代碼和對API的探究完善對castle dynamicproxy的了解....


Castle DynmaicProxy API提供了什么

首先這里要說明框架當中幾個重要的對象:
1、IInterceptor?攔截器該接口提供了可以自定義攔截器邏輯的入口,實現接口的 Intercept 方法即可,在后面創建代理對象需要接口實例來控制代理對象行為,這里框架也提供了StandardInterceptor標準的攔截器繼承MarshalByRefObject對象以及實現了IInterceptor接口,它包含了protected virtual void PerformProceed(IInvocation invocation);protected virtual void PostProceed(IInvocation invocation);protected virtual void PreProceed(IInvocation invocation);三個常用的接口方法....

public interface IInterceptor{ ? ?void Intercept(IInvocation invocation); }

2、IProxyGenerator?代理對象的創建者,包含了兩個屬性:Logger?、ProxyBuilder(只讀,具體由它來創建代理類型),包含如下幾個重要的API方法:
(1)動態創建類代理?proxyGenerator.CreateClassProxy 包含重載:Creates proxy object intercepting calls to virtual members of type TClass on newly created instance of that type with given interceptors. (創建一個新的代理對象subclass,通過配置的攔截器攔截原始對象標記了公開public的虛方法virtual的method產生效果,包含使用方法傳遞進來的代理配置項);
(2)動態創建類代理通過既有實例?proxyGenerator.CreateClassProxyWithTarget 包含重載:Creates proxy object intercepting calls to virtual members of type TClass on newly created instance of that type with given interceptors.(創建一個新的代理對象subclass,通過配置的攔截器攔截原始對象標記了公開public的虛方法virtual的method產生效果,提供了方法參數傳遞一個既有的目標實例對象,包含使用方法傳遞進來的代理配置項);
(3)動態創建接口代理不需要實現接口的實例對象?proxyGenerator.CreateInterfaceProxyWithoutTarget:Creates proxy object intercepting calls to members of interface TInterface on target object generated at runtime with given interceptor.(動態創建接口對象實現的實例且不需要實現了接口實例參數,通過攔截器湊效于接口方法實現攔截,注意這里如果接口方法要求了返回值,就需要在攔截器中指定返回值,類似于:invocation.ReturnValue=2;
(4)動態創建接口代理通過實現接口的實例對象?proxyGenerator.CreateInterfaceProxyWithTarget:Creates proxy object intercepting calls to members of interface TInterface on target object with given interceptors.(動態創建接口代理對象,通過傳遞實現了接口的對象實例,使用配置的攔截器對象作用于接口的每個方法,這里實現接口的對象實例的實現方法就可以不需要配置為vritual了,因為在接口代理對象中已經包裹住了原始對象是采用了類似于注入的方式而不是繼承,可以參考下面的示例代碼
(5)動態創建接口代理通過實現了接口的實例對象?proxyGenerator.CreateInterfaceProxyWithTargetInterface:Creates proxy object intercepting calls to members of interface TInterface on target object with given interceptors. Interceptors can use Castle.DynamicProxy.IChangeProxyTarget interface to provide other target for method invocation than default target.(與上述CreateInterfaceProxyWithTarget相似,那么它們的區別在于哪里吶,這里博主本著研究的精神找了一下,不至于翻看源碼了....,找到類似代碼提供者的一點描述如下:http://kozmic.net/2009/11/13/interfaceproxywithtarget-interfaceproxywithtargetinterface-ndash-whatrsquos-the-difference/,大體總結就是:一般情況下調用者需要的就是CreateInterfaceProxyWithTargetInterface這個API的調用,其中它提供了兩個優點:當使用InterfaceProxyWithTargetInterface時,它的調用實現了IChangeProxyTarget接口,該接口允許攔截途中更改目標對象。然后最重要的是 InterfaceProxyWithTargetInterface更好地使用緩存,詳情參考鏈接代碼驗證過程,當然博主也親自測試如同文中作者如出一轍!!。)博主實驗參考如下:

?IMyCompare oneCompare = proxyGenerator.CreateInterfaceProxyWithTarget<IMyCompare>(new MyCompareOne(), new MyStandradInterceptor());IMyCompare twoCompare = proxyGenerator.CreateInterfaceProxyWithTarget<IMyCompare>(new MyCompareTwo(), new MyStandradInterceptor());Type oneCompareType = oneCompare.GetType();Type twoCompareType = twoCompare.GetType();Console.WriteLine("{0}", object.ReferenceEquals(oneCompareType, twoCompareType)); // falseIMyCompare oneCompare1 = proxyGenerator.CreateInterfaceProxyWithTargetInterface<IMyCompare>(new MyCompareOne(), new MyStandradInterceptor());IMyCompare twoCompare1 = proxyGenerator.CreateInterfaceProxyWithTargetInterface<IMyCompare>(new MyCompareTwo(), new MyStandradInterceptor());Type oneCompare1Type = oneCompare1.GetType();Type twoCompare1Type = twoCompare1.GetType();Console.WriteLine("{0}", object.ReferenceEquals(oneCompare1Type, twoCompare1Type)); // ture

關于castle dynamicproxy動態代理中的對與class與interface的處理方式大致原理探究關于class的代理,相信很多同學都應該直到,也就是設計模式當中的代理模式的利用,具體就是繼承原始類對象實現對原始對象虛方法的重寫實現注入攔截的邏輯,不過這一切都是動態的不需要我們去構建了,參考代碼如下(這里只考慮一級代理,多級也就是多層繼承關系):

public class Caller{ ? ? ? ?public virtual void Call() ? ? ? ?{Console.WriteLine("calling...");}} ? ?public class CallerProxy : Caller{ ? ? ? ?public override void Call() ? ? ? ?{ ? ? ? ? ? ?// 執行前Console.WriteLine("pre action"); ? ? ? ? ? ?base.Call(); ? ? ? ? ? ?// 執行后Console.WriteLine("post action");}} ? ?static void Main(string[] args) ? ? ? ?{Caller caller = new CallerProxy();caller.Call(); Console.ReadKey();}

接著關于interface的代理的大致原型是,通過實現接口產生一個包含了傳遞的接口實習實例對象的接口代理對象,可能這里有點繞,不過依然還是代理模式,關系從繼承變成了包含,同理這些東西castle已經幫我用動態的方式構建好了,我們看一示例代碼就知道了,這里展示一層代理多級代理就是層層包含了

public interface IService{ ? ? ? ?void Process();} ? ?public class Service : IService{ ? ? ? ?public void Process() ? ? ? ?{ ? ? ? ? ? ?// do somethingConsole.WriteLine("processing...");}} ? ?public class ServiceProxy : IService{ ? ? ? ?private readonly Service _service; ? ? ? ?public ServiceProxy(Service service) ? ? ? ?{_service = service;} ? ? ? ?public void Process() ? ? ? ?{ ? ? ? ? ? ?// pre actionConsole.WriteLine("pre action");_service.Process(); ? ? ? ? ? ?// post actionConsole.WriteLine("post action");}}IService service = new ServiceProxy(new Service());service.Process();Console.ReadKey();

相信看到這里你也覺得,我去,動態代理這么簡單么,其實不然,雖然道理大家一看就懂是簡單就是設計模式的代理模式的運用嘛,但是將這個動作泛化為一個通用的API支持可變攔截器數量配置以及各種代理配置將是一項繁雜而小心的工作,既要考慮友好的API還有重中之重的性能保證,也就是使用上面提到的 Reflect.Emit + OpCode 來實現接近于元數據編程....,可怕!!!所以,這里給寫AOP的同學點贊!@Lemon
3、代理生成配置對象?ProxyGenerationOptions,在生成代理對象是可傳遞自定義配置對象,實現可控的攔截,該對象主要配置項:

public IProxyGenerationHook Hook { get; set; } (決定了該方法是否受攔截器攔截,可以實現自定義Hook)
public IInterceptorSelector Selector { get; set; } (決定了該方法受那些攔截器攔截,可以實現自定義Selector)
public Type BaseTypeForInterfaceProxy { get; set; } (決定了接口代理的基礎類型,詳情使用參考鏈接:https://stackoverflow.com/questions/2969274/castle-dynamicproxy-how-to-proxy-equals-when-proxying-an-interface)

具體例子參考如下:

public class MyInterceptorSelector : IInterceptorSelector{ ? ? ? ?public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors){ ? ? ? ? ? ?if (method.Name.EndsWith("Repository")){ ? ? ? ? ? ? ? ?return interceptors.Where(x => x is TestInterceptor).ToArray();} ? ? ? ? ? ?return interceptors.Where(x => x is TestInterceptor2 || x is MyStandradInterceptor).ToArray();}} public class MyGenerationProxyHook : IProxyGenerationHook{ ? ? ? ?public void MethodsInspected() ? ? ? ?{} ? ? ? ?public void NonProxyableMemberNotification(Type type, MemberInfo memberInfo) ? ? ? ?{} ? ? ? ?public bool ShouldInterceptMethod(Type type, MethodInfo methodInfo) ? ? ? ?{ ? ? ? ? ? ?return methodInfo.Name.EndsWith("Service");}}

這里相信大家一看就明白了,就不多說了.....這里注意點就是:?攔截器的植入順序與生效順序是一致的....


從其他框架復盤認識castle與集成

了解過[abp]:(https://aspnetboilerplate.com/) 的同學,肯定就知道此框架強制把castle家族的castle.core+Castle.Windsor(依賴前者)融入進abp當中了,采用了接口代理實現了日志記錄、異常處理、權限審查、審計日志等等,當然abp框架當中確實不止一處是值得我們學習,不過此框架構建在一個及集眾家之所長的情況下就顯得復雜,從知名度可以看出選擇caslte來作為aop+ioc的集成是個不錯的選擇,接著從moq的部分實例代碼中可以看出,就是利用了proxyGenerator.CreateInterfaceProxyWithoutTarget等等,之類的細節就可以回想起原始API做起底層的支撐作用,再者castle與許多第三方ioc有著比較好的集成,例如比較出名的ioc框架autofac+castle也是很多人的選擇,參考鏈接:[AOP系列]Autofac+Castle實現AOP事務:http://www.cnblogs.com/jianxuanbing/p/7199457.html
這里博主為何要把ioc扯進來一起說吶?
從castle的api來看大家覺得有木有點覺著好像有著一點ioc的功能,但是為何我們要明確概念說aop是aop,ioc是ioc吶,這里博主的理解就是,它們各自的職責是不同,它們各自只需要各司其職就行了,也不要越界也是編程開發當中的單一職責的體現了,雖然多多少少大家都有些動態創建對象那么回事兒(不管是emit還是activor.CreateInstance)!但是我們從ioc的職能分析得到的是:1、負責對象的存儲(注冊)與創建(解析) 2、負責對象的依賴關系維護 3、負責對象的生命周期,這三點就可以看出與AOP功能不一致,但是我們看到對象創建這個時候,想一下是否可以在對象創建的時植入Interceptor???回答是:肯定是可以的,所以這就是為何我們常常把ioc與aop一起來食用了,聽說這樣用更配哦!!


總結

我們從AOP的定義到AOP的使用場景,然后.net下面的AOP的介紹,接著重點介紹了動態代理中的castle.core的官方說明與文檔,后面尤其重要的詳解了框架當中重要的一些對象和API以及原理和實踐,也途中參考一些文章和stackoverflow,也請教AOP相關人士,這里感謝!好了,時間也不早了,相信學無止境,那么就保持持續學習,持續內化知識,就像修煉內功心法一樣,半途而廢還容易走火入魔,一知半解說出去的東西自己都沒搞明白,豈不是笑話了!!加油吧,騷年

這里引用知乎大大的一段話以此激勵:成長必須經歷一個步驟,就是把知識內化成能力。知識是用腦記住的,能力是用手練習出來的。在工作的幾年里,我們可能看過很多書,聽過很多技術講座和視頻,但是通過聽和看只是讓你能記住這些知識,這些知識還不能轉換成你的能力。聽和看只是第一步,更重要的是實踐,通過刻意練習把聽到和看到的知識內化成你的能力。刻意練習,就是有目的的練習,先規劃好,再去練習。首先給自己定一個目標,目標可以有效的引導你學習,然后使用3F練習法:1: 專注(Focus),專注在眼前的任務上,在學習過程中保持專注,可以嘗試使用番茄工作法。2:反饋(Feedback),意識到自己的不足,學習完之后進行反思,思考下自己哪些方面不足,為什么不足,3: 修正(Fix),改進自己的不足。不停的練習和思考可以改變大腦結構,大腦像肌肉一樣,挑戰越大,影響越大,學習更高效,并且也會產生突破性。?-- 原始鏈接:?https://www.zhihu.com/question/26572626/answer/246901769?utm_medium=social&utm_source=qq


收集備注園中相關AOP好文

1、Asp.Net Core輕量級Aop解決方案:AspectCore?http://www.cnblogs.com/liuhaoyang/p/aspectcore-introduction-1.html
2、C# 實現AOP 的幾種常見方式?:?http://www.cnblogs.com/zuowj/p/7501896.html
3、.Net基于RealProxy實現AOP?:?http://www.cnblogs.com/lflyq/p/6286925.html
4、Aspect-Oriented Programming : 使用 RealProxy 類進行面向方面的編程?:?https://msdn.microsoft.com/zh-cn/magazine/dn574804.aspx


補充更新(2017年10月20日15:07:33)

關于 castle api 當中對于?IInvocation對象的解釋少了一些,這里補充一下:
1、invocation.Arguments: 方法執行被攔截時的方法參數數組
2、invocation.GenericArguments: 被攔截方法的泛型參數類型數組,如果沒有就是null
3、invocation.InvocationTarget: 獲取當前執行的目標對象,例如:如果是class代理就是YourClassProxy,接口代理就是實現接口的對象實例,如果沒有則為null,也就是當使用xxxWithoutTarget的時候
4、invocation.Method:獲取代理對象的方法信息,例如:如果是class代理的時候就是YourClass.YourMethod對象的MethodInfo且這個時候invocation.Method == invocation.MethodInvocationTarget;如果是interface代理就是接口對象的方法信息,例如:ICall.Call 這個方法的MethodInfo信息且這個時候invocation.Method != invocation.MethodInvocationTarget,因為invocation.MethodInvocationTarget是接口對應實現的目標對象的方法信息,也就是例如:MyCall.Call 方法對應上面的 ICall 接口來說,當然也可以使用 WithoutTarget方式,這樣就會導致?invocation.MethodInvocationTarget==null的情況
5、invocation.MethodInvocationTarget: 指向真正的目標對象的方法信息MethodInfo,大致可以根據第四點給出了說明
6、invocation.Proxy?: 獲取攔截時的代理對象,例如:YourClassProxy(類代理) 或者 ICallProxy(接口代理) 對象
7、invocation.ResultValue: 獲取或者設置代理方法的返回值
8、invocation.TargetType: 獲取真實目標對象的類型
9、invocation.GetArgumentValue(int index);: 通過index獲取參數值
10、invocation.GetConcreteMethod();: 同理第四點
11、invocation.GetConcreteMethodInvocationTarget();: 同理第五點
12、invocation.Proceed();: 調用下一個攔截器直到目標方法
13、invocation.SetArgumentValue(int index, object value);: 設置更改參數值通過下標
這里博主列舉除了攔截途中對象IInvocation的所有成員,大家在使用攔截器的時候可根據自己邏輯使用以上API到達要求!

原文地址:http://www.cnblogs.com/DjlNet/p/7603654.html


.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注

總結

以上是生活随笔為你收集整理的框架学习与探究之AOP--Castle DynamicProxy的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。