全新升級的AOP框架Dora.Interception[4]: 基于表達式的攔截器注冊

基于特性標注的攔截器注冊方式僅限于將攔截器應用到自己定義的類型上,對于第三方提供的類型就無能為力了。對于Dora.Interception來說,攔截器注冊本質上建立攔截器與一個或者多個目標方法之間的映射,所以最笨的方式就是利用反射的方式得到表示目標方法的MethodInfo對象,并將它與對應的攔截器關聯在一起。這種方式雖然能夠解決問題,但是編程體驗很差。本篇介紹的基于表達式的攔截器注冊方式利用針對目標方法或者屬性的調用表達式,以一種強類型的方式獲取到目標方法,極大地改善了編程體驗。

一、IInterceptorRegistry

以表達式采用強類型的方式將指定類型的攔截器應用到目標方法上是借助如下這個IInterceptorRegistry接口完成的。IInterceptorRegistry接口提供了一個For<TInterceptor>方法以待注冊的攔截器類型關聯,參數arguments用來提供構建攔截器對象的參數。該方法會返回一個IInterceptorRegistry<TInterceptor>對象,它提供了一系列的方法幫助我們將指定的攔截器應用到指定目標類型(通過泛型參數類型TTarget表示)相應的方法上。

public?interface?IInterceptorRegistry
{IInterceptorRegistry<TInterceptor>?For<TInterceptor>(params?object[]?arguments);...
}public?interface?IInterceptorRegistry<TInterceptor>
{IInterceptorRegistry<TInterceptor>?ToAllMethods<TTarget>(int?order);IInterceptorRegistry<TInterceptor>?ToMethod<TTarget>(int?order,?Expression<Action<TTarget>>?methodCall);IInterceptorRegistry<TInterceptor>?ToMethod(int?order,?Type?targetType,?MethodInfo?method);IInterceptorRegistry<TInterceptor>?ToGetMethod<TTarget>(int?order,?Expression<Func<TTarget,?object?>>?propertyAccessor);IInterceptorRegistry<TInterceptor>?ToSetMethod<TTarget>(int?order,?Expression<Func<TTarget,?object?>>?propertyAccessor);IInterceptorRegistry<TInterceptor>?ToProperty<TTarget>(int?order,?Expression<Func<TTarget,?object?>>?propertyAccessor);
}

封裝了IServiceCollection集合的InterceptionBuilder提供了一個RegisterInterceptors擴展方法,我們可以利用該方法定義的Action<IInterceptorRegistry>類型的參數來使用上述的這個IInterceptorRegistry接口。不論是IServiceCollection接口的BuildInterceptableServiceProvider擴展方法,還是IHostBuilder接口的UseInterception方法均提供了一個可選的Action<InterceptionBuilder>委托類型的參數。

public?sealed?class?InterceptionBuilder
{public?IServiceCollection?Services?{?get;?}public?InterceptionBuilder(IServiceCollection?services);??
}public?static?class?Extensions
{public?static?InterceptionBuilder?RegisterInterceptors(this?InterceptionBuilder?builder,?Action<IInterceptorRegistry>?register);public?static?IServiceProvider?BuildInterceptableServiceProvider(this?IServiceCollection?services,?Action<InterceptionBuilder>??setup?=?null);public?static?IHostBuilder?UseInterception(this?IHostBuilder?hostBuilder,?Action<InterceptionBuilder>??setup?=?null);
}

二、將攔截器應用到某個類型

類似與將InterceptorAttribute標注到某個類型上,我們也可以采用這種方式將指定的攔截器應用到目標類型上,背后的含義就是應用到該類型可以被攔截的所以方法上(含屬性方法)。

public?class?FoobarInterceptor
{public?ValueTask?InvokeAsync(InvocationContext?invocationContext){var?method?=?invocationContext.MethodInfo;Console.WriteLine($"{method.DeclaringType!.Name}.{method.Name}?is?intercepted.");return?invocationContext.ProceedAsync();}
}public?class?Foobar
{public?virtual?void?M()?{?}public?virtual?object??P?{?get;?set;?}
}

我們可以采用如下的方式將調用IInterceptorRegistry<TInterceptor>的ToAllMethods<TTarget>方法將上面定義的攔截器FoobarInterceptor應用到Foobar類型的所有方法上。

var?foobar?=?new?ServiceCollection().AddSingleton<Foobar>().BuildInterceptableServiceProvider(interception?=>?interception.RegisterInterceptors(RegisterInterceptors)).GetRequiredService<Foobar>();foobar.M();
foobar.P?=?null;
_?=?foobar.P;static?void?RegisterInterceptors(IInterceptorRegistry?registry)
{var?foobar?=?registry.For<FoobarInterceptor>();foobar.ToAllMethods<Foobar>(order:?1);
}

從如下所示的執行結果可以看出,Foobar類型的M方法和P屬性均被FoobarInterceptor攔截下來(源代碼)。

741f6118b6ad6a71614c9a999dd3c87a.png

三、應用到指定的方法和屬性

我們可以通過指定調用方法或者獲取屬性的表達式來指定攔截器應用的目標方法。我們將目標類型Foobar定義成如下的形式,兩個重載的M方法和三個屬性均是可以攔截的。

public?class?Foobar
{public?virtual?void?M(int?x,?int?y)?{?}public?virtual?void?M(double?x,?double?y)?{?}public?virtual?object??P1?{?get;?set;?}public?virtual?object??P2?{?get;?set;?}public?virtual?object??P3?{?get;?set;?}
}

我們利用如下的代碼將上面定義的FoobarInterceptor應用到Foobar類型相應的成員上。具體來說,我們調用ToMethod<TTarget>方法應用到兩個重載的M方法,調用ToProperty<TTarget>方法應用到P1屬性的Get和Set方法上,調用ToGetMethod<TTarget>和ToSetMethod<TTarget>方法應用到P2屬性的Get方法和P3屬性的Set方法。

var?provider?=?new?ServiceCollection().AddSingleton<Foobar>().BuildInterceptableServiceProvider(interception?=>?interception.RegisterInterceptors(RegisterInterceptors));var?foobar?=?provider.GetRequiredService<Foobar>();foobar.M(1,?1);
foobar.M(3.14,?3.14);
foobar.P1?=?null;
_?=?foobar.P1;
foobar.P2?=?null;
_?=?foobar.P2;
foobar.P3?=?null;
_?=?foobar.P3;
Console.ReadLine();static?void?RegisterInterceptors(IInterceptorRegistry?registry)
{var?foobar?=?registry.For<FoobarInterceptor>();foobar.ToMethod<Foobar>(order:?1,?it?=>?it.M(default(int),?default(int))).ToMethod<Foobar>(order:?1,?it?=>?it.M(default(double),?default(double))).ToProperty<Foobar>(order:?1,?it?=>?it.P1).ToGetMethod<Foobar>(order:?1,?it?=>?it.P2).ToSetMethod<Foobar>(order:?1,?it?=>?it.P3);
}

程序運行后,針對Foobar相應成員的攔截體現在如下所示的輸出結果上(源代碼)。

6111d37bfd769af4de347690e3b8cfde.png

四、指定構建攔截器的參數

如果應用的攔截器類型構造函數指定了參數,我們采用這種注冊方式的時候也可以指定參數。以如下這個FoobarInterceptor為例,其構造函數中指定了兩個參數,一個是代表攔截器名稱的name參數,另一個是IFoobar對象。

public?class?FoobarInterceptor
{public?FoobarInterceptor(string?name,?IFoobar?foobar){Name?=?name;Foobar?=?foobar;}public?string?Name?{?get;?}public?IFoobar?Foobar?{?get;?}public?ValueTask?InvokeAsync(InvocationContext?invocationContext){Console.WriteLine($"{invocationContext.MethodInfo.Name}?is?intercepted?by?FoobarInterceptor?{Name}.");Console.WriteLine($"Foobar?is?'{Foobar.GetType()}'.");return?invocationContext.ProceedAsync();}
}
public?interface?IFoobar?{?}
public?class?Foo?:?IFoobar?{?}
public?class?Bar:?IFoobar?{?}public?class?Invoker
{public?virtual?void?M1()?{?}public?virtual?void?M2()?{?}
}

由于字符串參數name無法從依賴注入容器提取,所以在注冊FoobarInterceptor是必須顯式指定。如果容器能夠提供IFoobar對象,但是希望指定一個不通過的對象,也可以在注冊的時候顯式指定一個IFoobar對象。我們按照如下的方式將兩個不同的FoobarInterceptor對象分別應用到Invoker類型的Invoke1和Invoke2方法上,并分別將名稱設置為Interceptor1和Interceptor2,第二個攔截器還指定了一個Bar對象作為參數(容器默認提供的IFoobar對象的類型為Foo)。

var?invoker?=?new?ServiceCollection().AddSingleton<Invoker>().AddSingleton<IFoobar,?Foo>().BuildInterceptableServiceProvider(interception?=>?interception.RegisterInterceptors(RegisterInterceptors)).GetRequiredService<Invoker>();invoker.M1();
Console.WriteLine();
invoker.M2();static?void?RegisterInterceptors(IInterceptorRegistry?registry)
{registry.For<FoobarInterceptor>("Interceptor1").ToMethod<Invoker>(order:?1,?it?=>?it.M1());registry.For<FoobarInterceptor>("Interceptor2",?new?Bar()).ToMethod<Invoker>(order:?1,?it?=>?it.M2());
}

程序運行之后,兩個FoobarInterceptor對象的名稱和依賴的IFoobar對象的類型以如下的形式輸出到控制臺上(源代碼)。

f76f2e9d1974c53ff1b82efb5912163f.png

五、攔截屏蔽

除了用來注冊指定攔截器的For<TInterceptor>方法,IInterceptorRegistry接口還定義了如下這些用來屏蔽攔截的SuppressXxx方法。

public?interface?IInterceptorRegistry
{IInterceptorRegistry<TInterceptor>?For<TInterceptor>(params?object[]?arguments);IInterceptorRegistry?SupressType<TTarget>();IInterceptorRegistry?SupressTypes(params?Type[]?types);IInterceptorRegistry?SupressMethod<TTarget>(Expression<Action<TTarget>>?methodCall);IInterceptorRegistry?SupressMethods(params?MethodInfo[]?methods);IInterceptorRegistry?SupressProperty<TTarget>(Expression<Func<TTarget,?object?>>?propertyAccessor);IInterceptorRegistry?SupressSetMethod<TTarget>(Expression<Func<TTarget,?object?>>?propertyAccessor);IInterceptorRegistry?SupressGetMethod<TTarget>(Expression<Func<TTarget,?object?>>?propertyAccessor);
}

我們可以采用如下的方式會將屏蔽掉Foobar類型所有成員的攔截特性,雖然攔截器FoobarInterceptor被注冊到了這個類型上(源代碼)。

var?foobar?=?new?ServiceCollection().AddSingleton<Foobar>().BuildInterceptableServiceProvider(interception?=>?interception.RegisterInterceptors(RegisterInterceptors)).GetRequiredService<Foobar>();
...static?void?RegisterInterceptors(IInterceptorRegistry?registry)
{registry.For<FoobarInterceptor>().ToAllMethods<Foobar>(order:?1);registry.SupressType<Foobar>();
}

下面的程序明確屏蔽掉Foobar類型如下這些方法的攔截能力:M方法,P1屬性的Get和Set方法(如果有)以及P屬性的Get方法(源代碼)。

var?foobar?=?new?ServiceCollection().AddSingleton<Foobar>().BuildInterceptableServiceProvider(interception?=>?interception.RegisterInterceptors(RegisterInterceptors)).GetRequiredService<Foobar>();...static?void?RegisterInterceptors(IInterceptorRegistry?registry)
{registry.For<FoobarInterceptor>().ToAllMethods<Foobar>(order:?1);registry.SupressMethod<Foobar>(it=>it.M());registry.SupressProperty<Foobar>(it?=>?it.P1);registry.SupressGetMethod<Foobar>(it?=>?it.P2);
}

六、兩個后備方法

通過指定調用目標方法或者提取屬性的表達式來提供攔截器應用的方法和需要屏蔽的方法提供了較好的編程體驗,但是能夠提供這種強類型編程模式的前提是目標方法或者屬性是公共成員。對于受保護(protected)的方法和屬性,我們只能使用如下兩個后備方法,指定代表目標方法的MethodInfo對象。

public?interface?IInterceptorRegistry<TInterceptor>
{IInterceptorRegistry<TInterceptor>?ToMethods<TTarget>(int?order,?params?MethodInfo[]?methods);
}public?interface?IInterceptorRegistry
{IInterceptorRegistry?SupressMethods(params?MethodInfo[]?methods);
}

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

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

相關文章

mysql8.0.12插件_MySQL8.0.12 安裝及配置

MySQL8.0.12 安裝及配置發布時間&#xff1a;2018-08-07 10:39,瀏覽次數&#xff1a;274, 標簽&#xff1a;MySQL一.安裝1.從網上下載MySQL8.0.12版本&#xff0c;下載地址&#xff1a;https://dev.mysql.com/downloads/mysql/2. 下載完成后解壓我解壓的路徑是&#xff1a;D:\J…

python模塊之hashlib

hashlib模塊實現了多種安全哈希和信息摘要算法的通用接口&#xff0c;包括FIPS中定義的SHA1, SHA224, SHA256, SHA384, SHA512以及RFC 1321中定義的MD5 注意點&#xff1a;1. adler32及crc32哈希由zlib模塊提供2. 某些算法已知存在哈希碰撞弱點 哈希算法 每個hash算法都有一個同…

記一次阿里電面經歷

昨天下午&#xff08;3/19&#xff09;三點多鐘&#xff0c;接到了一個杭州的電話&#xff0c;是阿里的。問我是否方便聊聊。我說我在上課&#xff0c;四點下課。然后他就四點多鐘的時候又打了一次過來。項目經歷上來就問我有無大型項目的經歷。不好意思&#xff0c;我說無。。…

C語言程序設計第三次作業

&#xff08;一&#xff09;改錯題 計算f(x)的值&#xff1a;輸入實數x&#xff0c;計算并輸出下列分段函數f(x)的值&#xff0c;輸出時保留1位小數。 輸入輸出樣例1&#xff1a;   Enterr x: 10.0   f(10.0) 0.1 輸入輸出樣例2&#xff1a;   Enter x: 234   f(234.0…

mysql數據庫項目化教程鄭小蓉_MySQL數據庫項目化教程(高等職業教育“十三五”規劃教材(軟件技術專業))...

《MySQL數據庫項目化教程/高等職業教育十三五規劃教材(軟件技術專業)》是一本介紹MySQL數據庫基礎知識的入門教材&#xff0c;采用項目驅動方式循序漸進地介紹MySQL各個模塊的知識。主要內容包括&#xff1a;Windows下MySQL的安裝&#xff0c;MySQL服務的啟動與停止&#xff0c…

WPF-09 ManualResetEventSlim信號量

業務場景如下&#xff1a;WPF應用程序后臺有個定時任務在接收PLC硬件數據(該線程接收完數據之后, 會重新啟動一個新線程繼續接收.....)&#xff0c;當應用程序關閉時, 我們得確保接收PLC硬件數據的線程完成之后,再關閉應用程序&#xff0c;否則會造成數據丟失。上面的業務場景是…

【bzoj3033】太鼓達人 DFS歐拉圖

題目描述 給出一個整數K&#xff0c;求一個最大的M&#xff0c;使得存在一個每個位置都是0或1的圈&#xff0c;圈上所有連續K位構成的二進制數兩兩不同。輸出最大的M以及這種情況下字典序最小的方案。 輸入 一個整數K。 輸出 一個整數M和一個二進制串&#xff0c;由一個空格分隔…

Redis 集合處理

學習了列表之后&#xff0c;發現了Redis處理字符串的功能強大。 為了適應不同場景的需求&#xff0c;還有一個用的很多的就是集合。 Redis提供的集合支持的類型是字符串。并且集合中的元素值是唯一的&#xff0c;也就是說不能出現重復數據。 而且&#xff0c;集合的實現是通過哈…

fpga mysql_FPGA的一些瑣碎知識整理

1.生產FPGA的廠家有&#xff1a;ALTERAXILINXATCELLatticeps:Altera和Xilinx主要生產一般用途FPGA&#xff0c;其主要產品采用SRAM工藝Actel主要提供非易失性FPGA&#xff0c;產品主要基于反熔絲工藝和FLASH工藝ps: 熔絲&#xff0c;顧名思義&#xff1a;把絲熔掉&#xff0c;反…

使用增量備份修復DG中的GAP

問題描述 oracle中DG出現主備不同步現象&#xff0c;alert日志報警有gap信息&#xff0c;但是v$archive_gap視圖查不到任何信息。同時主庫上的對應歸檔已經刪除且沒有備份 解決方案 1.查詢備庫的scn SQL> select current_scn from v$database; 這時有可能出來的scn是以科學計…

C# 反射類Assembly用法舉例

概述程序運行時&#xff0c;通過反射可以得到其它程序集或者自己程序集代碼的各種信息&#xff0c;包括類、函數、變量等來實例化它們&#xff0c;執行它們&#xff0c;操作它們&#xff0c;實際上就是獲取程序在內存中的映像&#xff0c;然后基于這個映像進行各種操作。Assemb…

團隊作業

團隊&組員&#xff1a; 沒有組名&#xff0c;大概是因為我們組雖然有10個人&#xff0c;但是好像只起到人多的地方就容易開車搞笑&#xff0c;沒有內涵&#xff0c;取出來的都是秋名山吳彥組這樣的開車組名&#xff0c;在大家的的強烈建議和玩笑中&#xff0c;決定了沒有組…

算法系列【希爾排序】篇

常見的內部排序算法有&#xff1a;插入排序、希爾排序、選擇排序、冒泡排序、歸并排序、快速排序、堆排序、基數排序等。用一張圖概括&#xff1a;關于時間復雜度&#xff1a;1. 平方階 (O(n2)) 排序各類簡單排序&#xff1a;直接插入、直接選擇和冒泡排序。2. 線性對數…

sql查詢索引語句_sql優化總結--基于sql語句優化和索引優化

概述最近做查詢&#xff0c;統計和匯總。由于數據量比較龐大&#xff0c;大部分表數據上百萬&#xff0c;甚至有的表數據上千萬。所以在系統中做sql優化比較多&#xff0c;特此寫一篇文章總結一下關于sql優化方面的經驗。導致查詢緩慢的原因1、數據量過大2、表設計不合理3、sql…

電商行業運維實踐

電商行業運維實踐&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;…

數據結構小總結(成都磨子橋技工學校數據結構前12題)

[pixiv] https://www.pixiv.net/member_illust.php?modemedium&illust_id34352147 暑假的作業&#xff0c;頹頹的我總算是寫完了 線段樹 線段樹是一個高級玩意&#xff0c;不僅可以求區間和&#xff0c;區間最大等等的簡單問題&#xff0c;靈活運用還有好多變種。自從學…

【九章算法免費講座第一期】轉專業找CS工作的“打狗棒法”

講座時間&#xff1a; 美西時間6月5日18&#xff1a;30-20&#xff1a;00&#xff08;周五&#xff09; 北京時間6月6日09&#xff1a;30-11&#xff1a;00&#xff08;周六a.m&#xff09; 講座安排&#xff1a; 免費在線直播講座 報名網址&#xff1a; http://t.cn/R2XgMSH&a…

golang mysql 防注入_Go,Gorm 和 Mysql 是如何防止 SQL 注入的

Go&#xff0c;Gorm 和 Mysql 是如何防止 SQL 注入的SQL 注入和 SQL 預編譯技術什么是 SQL 注入所謂SQL注入(sql inject)&#xff0c;就是通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串&#xff0c;最終達到欺騙服務器執行惡意的SQL命令。具體來說&#xff…

wav2midi 音樂旋律提取算法 附可執行demo

前面提及過&#xff0c;音頻指紋算法的思路。 也梳理開源了兩個比較經典的算法。 https://github.com/cpuimage/shazam https://github.com/cpuimage/AudioFingerprinter 后來一段時間&#xff0c;稍微看了下這兩個算法&#xff0c;還有不少可以精簡優化的空間。 例如抗噪&…

全新升級的AOP框架Dora.Interception[5]: 實現任意的攔截器注冊方式

Dora.Interception提供了兩種攔截器注冊方式&#xff0c;一種是利用標注在目標類型、屬性和方法上的InterceptorAttribute特性&#xff0c;另一種采用基于目標方法或者屬性的調用表達式。通過提供的擴展點&#xff0c;我們可以任何我們希望的攔截器注冊方式。目錄一、IIntercep…