FluentAspects -- 基於 Fluent API 的 Aop

WeihanLi發表於2020-05-02

FluentAspects -- 基於 Fluent API 的 Aop

Intro

上次我們做了一個簡單的 AOP 實現示例,但是實現起來主要是基於 Attribute 來做的,對於程式碼的侵入性太強,於是嘗試實現基於 Fluent API 的方式來做 AOP 。

抽象 InterceptorResolver

原來獲取方法執行的 Interceptor 是通過 Attribute 來獲取的,現在我們只需要將獲取 Interceptor 的邏輯抽象出來就可以實現不必依賴於 Attribute

方法執行上下文定義:

public interface IInvocation
{
    public MethodInfo ProxyMethod { get; }

    public object ProxyTarget { get; }

    public MethodInfo Method { get; }

    public object Target { get; }

    public object[] Arguments { get; }

    Type[] GenericArguments { get; }

    public object ReturnValue { get; set; }
}

方法攔截器 Interceptor 介面定義:

public interface IInterceptor
{
    Task Invoke(IInvocation invocation, Func<Task> next);
}

自定義 Interceptor 只需要繼承這個介面實現相應的邏輯就好了

獲取 IInterceptorResolver 介面定義:

public interface IInterceptorResolver
{
    IReadOnlyCollection<IInterceptor> ResolveInterceptors(IInvocation invocation);
}

原來基於 Attribute 獲取 Interceptor 的方式可以實現一個 AttributeInterceptorResolver

想要基於 Fluent API 來獲取 Interceptor ,只需要實現基於 Fluent API 的 InterceptorResolver 就可以了,具體的實現可以參考 FluentConfigInterceptorResolver

示例預覽

測試服務定義:

public interface ISvc1
{
    void Invoke();
}

public interface ISvc2
{
    void Invoke();
}

public class Svc2 : ISvc2
{
    public void Invoke()
    {
        Console.WriteLine($"invoking in {GetType().Name} ...");
    }

    public void Invoke2()
    {
        Console.WriteLine($"invoking in {GetType().Name} ...");
    }
}

public class Svc3
{
    public virtual void Invoke()
    {
        Console.WriteLine($"invoking in {GetType().Name} ...");
    }
}

public class Svc4
{
    public virtual void Invoke()
    {
        Console.WriteLine($"invoking in {GetType().Name} ...");
    }

    public void Invoke2()
    {
        Console.WriteLine($"invoking2 in {GetType().Name} ...");
    }

    public virtual void Invoke3()
    {
        Console.WriteLine($"invoking3 in {GetType().Name} ...");
    }
}

測試 Interceptor

internal class LogInterceptor : IInterceptor
{
    public async Task Invoke(IInvocation invocation, Func<Task> next)
    {
        Console.WriteLine($"invoke {invocation.ProxyMethod} in {GetType().Name} begin");
        await next();
        Console.WriteLine($"invoke {invocation.ProxyMethod} in {GetType().Name} end");
    }
}

測試程式碼:

public static void Main(string[] args)
{
    var services = new ServiceCollection();
    services.AddFluentAspects(options =>
    {
        // 為所有攔截的方法新增攔截器
        options.InterceptAll()
            .With<LogInterceptor>()
            ;
        // 對 Svc3 型別禁用攔截器
        options.NoInterceptType<Svc3>();
        // Svc4 型別的 Invoke3() 方法禁用攔截器
        options.NoInterceptMethod<Svc4>(s => s.Invoke3());
    });
    services.AddTransientProxy<Svc4>();
    var serviceProvider = services.BuildServiceProvider();
    var proxyFactory = serviceProvider.GetRequiredService<IProxyFactory>();

    var svc1 = proxyFactory.CreateProxy<ISvc1>();
    svc1.Invoke();
    Console.WriteLine();

    var svc2 = proxyFactory.CreateProxy<ISvc2, Svc2>();
    svc2.Invoke();
    Console.WriteLine();

    var svc3 = proxyFactory.CreateProxy<Svc3>();
    svc3.Invoke();
    Console.WriteLine();

    var svc4 = proxyFactory.CreateProxyWithTarget<ISvc2, Svc2>(new Svc2());
    svc4.Invoke();
    Console.WriteLine();

    // 直接從註冊的服務中獲取
    var svc5 = serviceProvider.GetRequiredService<Svc4>();
    svc5.Invoke();
    Console.WriteLine();
    svc5.Invoke2();
    Console.WriteLine();
    svc5.Invoke3();
    Console.WriteLine();

    Console.WriteLine("finished");
    Console.ReadLine();
}

輸出結果預覽:

More

最近十幾天的時間一直在搞這個,相比之前寫的示例,真正實現一個完整的 AOP 框架還是要做比較多的事情的,之前的 AOP 示例,沒有考慮泛型,也沒有什麼設計,所以前面的示例只能算是一個小玩具。

在實現的過程中,參考了很多 AspectCore 的程式碼,有一些程式碼甚至是直接從 AspectCore 裡抄過來的。

推薦大家有機會研究學習一下檸檬大佬的 AspectCore 的原始碼,這個 AOP 框架的程式碼組織,程式碼細節都挺不錯的。

AspectCore 原始碼地址: https://github.com/dotnetcore/AspectCore-Framework

Reference

相關文章