Dora.Interception,為.NET Core度身打造的AOP框架:全新的版本
Dora.Interception 1.0(Github地址:可以訪問GitHub地址:https://github.com/jiangjinnan/Dora)推出有一段時間了,最近花了點時間將它升級到2.0,主要有如下的改進:
- 提供了原生的動態代理生成底層框架Dora.DynamicProxy:之前依賴第三方框架Castle實現最底層的代理生成,但是它不支持基於Task的並行編程(也就是說通過它編寫的Interceptor無法實現異步執行),所以我采用IL Emit的方式自行實現了這部分的功能,這些底層的功能實現在Dora.DynamicProxy中。
- 提供了如下兩種形式的攔截方案:
- 基於實例封裝:如果消費的類型是一個接口
- 基於類型繼承:如果目標類型是一個非Sealed類型,一個繼承與它的代理類型會被動態生成。如果Interceptor被應用到目標類型的某個虛方法或者屬性上,該成員會在生成的代理類中被重寫,進而使Interceptor得以執行。這種攔截機制適合非Sealed類型,只有虛方法/屬性能夠被攔截。
- 提供了針對屬性的攔截支持:之前的版本支持針對方法的攔截,最新版本中提供了針對屬性的攔截支持。我們可以選擇將Interceptor應用到某個類型的屬性上,也可以單獨應用到該屬性的Get或者Set方法上。
一、對基於Task的並行編程的支持
由於Dora.Interception將Dora.DynamicProxy作為默認的動態代理類型生成框架,所以不在依賴任何第三發框架,因此在編程會變得更加簡單,現在我們來做一個簡單的演示。在安裝了最新版本的NuGet包Dora.Interception之後,我們可以按照 “約定” 的方式來定義如下這麽一個簡單的Interceptor類型。為了驗證針對Task並行編程的支持,我們特意在攔截方法InvokeAsync中Delay了一秒鐘。
public class FoobarInterceptor { private InterceptDelegate _next;public FoobarInterceptor(InterceptDelegate next) { _next = next; } public async Task InvokeAsync(InvocationContext context) { Console.WriteLine("Interception task starts."); await Task.Delay(1000); Console.WriteLine("Interception task completes."); await _next(context); } }
我將Interceptor和Interceptor的註冊特意區分開來,Interceptor的註冊默認采用特性標註的形式來實現,為此我們為上面定義的FoobarInterceptor創建一個對應的特性類型FoobarAttribute。如下面的代碼片段所示,FoobarAttribute派生於InterceptorAttribute,FoobarInterceptor在重寫的Use方法中被構建,在構建過程中可以指定該Interceptor在整個Interceptor Chain的位置(Order)。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property)] public class FoobarAttribute : InterceptorAttribute { public override void Use(IInterceptorChainBuilder builder) { builder.Use<FoobarInterceptor>(this.Order); } }
接下來我們定義了簡單的類型Demo來使用FoobarInterceptor,Demo實現了接口IDemo,FoobarAttribute標註在需要被攔截的方法InvokeAsync上。
public interface IDemo { Task InvokeAsync(); } public class Demo : IDemo { [Foobar] public Task InvokeAsync() { Console.WriteLine("Target method is invoked."); return Task.CompletedTask; } }
由於Dora.Interception實現了與.NET Core的Dependency Injection的無縫集成,所以我們只需要采用我們熟悉的方式來提供服務實例就可以了。如下面的代碼片段所示,我們將IDemo和Demo之間的映射關系註冊到創建的ServiceCollection上之後,並沒有調用BuildeServiceProvider方法,而是調用BuildInterceptableServiceProvider來創建提供服務的ServiceProvider。
class Program { static void Main(string[] args) { var demo = new ServiceCollection() .AddSingleton<IDemo, Demo>() .BuildeInterceptableServiceProvider() .GetRequiredService<IDemo>(); demo.InvokeAsync(); Console.WriteLine("Continue..."); Console.Read(); } }
如下所示的是這段代碼的執行結果,我們可以看到應用的FoobarInterceptor被正常執行,而且它完全是以異步的方式執行的。
二、基於虛方法的攔截
如果Demo沒有實現任何的接口,並且它不是一個Sealed類型,它的虛方法和屬性也是可以被攔截的。比如我們將Demo做了如下的改動。
public class Demo { [Foobar] public virtual Task InvokeAsync() { Console.WriteLine("Target method is invoked."); return Task.CompletedTask; } }
所有Demo沒有了接口實現,所以我們需要對服務註冊代碼做相應的修改。執行修後的代碼,我們依然會得到相同的輸出。
class Program { static void Main(string[] args) { var demo = new ServiceCollection() .AddSingleton<Demo, Demo>() .BuildeInterceptableServiceProvider() .GetRequiredService<Demo>(); demo.InvokeAsync(); Console.WriteLine("Continue..."); Console.Read(); } }
三、屬性也可被攔截
對於上一版本來說,被攔截的成員僅限於普通的方法,最新的版本增加對屬性的支持。如果一個Interceptor被直接應用到某個屬性上,它實際上會被同時應用到該屬性的Get和Set方法上。比如我們在Demo類型上添加一個Value屬性,並在上面標準FoobarAttribute。
public class Demo { [Foobar] public virtual object Value { get; set; } }
接下來我們按照如下的方式獲取一個Demo對象,並調用其Value屬性的Set和Get方法。
class Program { static void Main(string[] args) { var demo = new ServiceCollection() .AddSingleton<Demo, Demo>() .BuildInterceptableServiceProvider() .GetRequiredService<Demo>(); Console.WriteLine("Set..."); demo.Value = new object(); Console.WriteLine("Get..."); var value = demo.Value; Console.Read(); } }
從如下的輸出結果可以看出,我們註冊到Value屬性上的FoobarInterceptor在Get和Set方法被調用的時候都執行了一遍。
如果我們只需要在某個屬性的Get或者Set方法上應用某個Interceptor,我們也可以作針對性的標註。在如下的代碼片段中,我們將FoobarAttrbute標準到Get方法上。
public class Demo { public virtual object Value { [Foobar] get; set; } }
再次執行程序,我們會發現FoobarInterceptor僅僅在調用Value屬性的Get方法時被執行了一次。
Dora.Interception,為.NET Core度身打造的AOP框架:全新的版本