1. 程式人生 > >Dora.Interception,為.NET Core度身打造的AOP框架:全新的版本

Dora.Interception,為.NET Core度身打造的AOP框架:全新的版本

分享 ide 1.0 nuget hub tex 普通 inb .class

Dora.Interception 1.0(Github地址:可以訪問GitHub地址:https://github.com/jiangjinnan/Dora)推出有一段時間了,最近花了點時間將它升級到2.0,主要有如下的改進:

  • 提供了原生的動態代理生成底層框架Dora.DynamicProxy:之前依賴第三方框架Castle實現最底層的代理生成,但是它不支持基於Task的並行編程(也就是說通過它編寫的Interceptor無法實現異步執行),所以我采用IL Emit的方式自行實現了這部分的功能,這些底層的功能實現在Dora.DynamicProxy中。
  • 提供了如下兩種形式的攔截方案:
    • 基於實例封裝:如果消費的類型是一個接口
      ,那麽提供的類型為動態生成的代理類,該代理類封裝了目標對象。對於每一個動態生成的接口實現成員來說,它會負責執行應用的Interceptor。如果需要調用目標方法,被封裝的目標對象的對應方法會被調用。這種攔截方案要求目標類型實現一個接口,接口中定義的所有方法和屬性都是可以被攔截的。
    • 基於類型繼承:如果目標類型是一個非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框架:全新的版本