Norns.Urd 中的一些设计

Norns.Urd 是什么?

Norns.Urd 是一个基于emit实现动态代理的轻量级AOP框架.
版本基于 netstandard2.0. 因此哪些.net 版本能用你懂的。
完成这个框架的目的主要出自于我的如下意愿:html

  • 静态AOP和动态AOP都实现一次
  • 若是不实现DI,怎么将AOP框架实现与其余现有DI框架集成
  • 一个AOP 如何将 sync 和 async 方法同时兼容且如何将实现选择权彻底交予用户

但愿该库能对你们有些小小的做用
中文文档在:https://fs7744.github.io/Norns.Urd/zh-cn/index.html
顺便求个star, github 地址:https://github.com/fs7744/Norns.Urdgit

对了,若是不了解AOP的同窗,能够看看这些文章:
面向切面的程序设计
什么是面向切面编程AOP?
AOP 有几种实现方式?github

Norns.Urd 中的一些设计

Norns.Urd的实现前提

因为Norns.Urd的实现基于如下两点前提编程

  1. 将 sync 和 async 方法同时兼容且如何将实现选择权彻底交予用户app

    • 其实这点还好,工做量变成两倍多一些就好,sync 和 async 彻底拆分红两套实现。
    • 提供给用户的Interceptor接口要提供 sync 和 async 混合在一套实现代码的方案,毕竟不能强迫用户实现两套代码,不少场景用户不须要为sync 和 async 的差别而实现两套代码
  2. 不包含任何内置DI,但要总体都为支持DI而做框架

    • 其实若是内置DI容器可让支持 generic 场景变得很是简单,毕竟从DI容器中实例化对象时必须有明确的类型,可是呢,如今已经有了那么多实现的库了,我就不想为了一些场景而实现不少功能(我真的懒,不然这个库也不会写那么久了)
    • 可是DI容器确实解耦很是棒,我本身都经常所以受益而减小了不少代码修改量,因此作一个aop库必需要考虑基于DI容器作支持,这样的话,di 支持的 open generic / 自定义实例化方法都要作支持,而且aop里面还得提供用户调用DI的方法,不然还很差用了 (这样算下来,我真的偷懒了吗?我是否是在给本身挖坑呀?)

如何设计解决的?

目前方案不必定完美,暂时算解决了问题而已 (有更好方案请必定要告诉我,我迫切须要学习)异步

提供什么样的拦截器编写模式给用户?

之前接触一些其余aop实现框架,不少都须要将拦截代码分为 方法前 / 方法后 / 有异常等等,我的以为这样的形式仍是必定程度上影响拦截器实现的代码思路,总以为不够顺滑async

可是像 ASP.NET Core Middleware就感受很是不错,以下图和代码:函数

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/index/_static/request-delegate-pipeline.png?view=aspnetcore-5.0

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello, World!");
});

拦截器也应该能够像这样作,因此拦截器的代码应该能够像这样:性能

public class ConsoleInterceptor 
{
    public async Task InvokeAsync(Context context, Delegate next)
    {
        Console.WriteLine("Hello, World!");
        await next(context);
    }
}

sync 和 async 方法如何拆分?又如何能合并在一块儿呢?用户有怎么本身选择实现sync 仍是 async 或者两个都都实现呢?

public delegate Task AsyncAspectDelegate(AspectContext context);

public delegate void AspectDelegate(AspectContext context);

// 拆分: 
// 由AspectDelegate 和 AsyncAspectDelegate 创建两套彻底区分 sync 和 async 的Middleware调用链,具体使用哪一个由具体被拦截的方法自己决定

public abstract class AbstractInterceptor : IInterceptor
{
    public virtual void Invoke(AspectContext context, AspectDelegate next)
    {
        InvokeAsync(context, c =>
        {
            next(c);
            return Task.CompletedTask;
        }).ConfigureAwait(false)
                    .GetAwaiter()
                    .GetResult();
    }

// 合并:
// 默认实现转换方法内容,这样各类拦截器均可以混在一个Middleware调用链中

    public abstract Task InvokeAsync(AspectContext context, AsyncAspectDelegate next);

// 用户自主性选择:
// 同时提供sync 和 async 拦截器方法能够重载,用户就能够本身选择了
// 因此用户在 async 中能够调用专门的未异步优化代码了,也不用说在 sync 中必须 awit 会影响性能了,
// 你认为影响性能,你在意就本身都重载,不在意那就本身选
}

没有内置DI,如何兼容其余DI框架呢?

DI框架都有注册类型,咱们能够经过 emit 生成代理类,替换本来的注册,就能够作到兼容。

固然每种DI框架都须要定制化的实现一些代码才能支持(唉,又是工做量呀)

AddTransient<IMTest>(x => new NMTest()), 相似这样的实例化方法怎么支持呢?

因为这种DI框架的用法,没法经过Func函数拿到实际会使用的类型,只能根据IMTest定义经过emit 生成 桥接代理类型,其伪码相似以下:

interface IMTest
{
    int Get(int i);
}

class IMTestProxy : IMTest
{
    IMTest instance = (x => new NMTest())();

    int Get(int i) => instance.Get(i);
}

.AddTransient(typeof(IGenericTest<,>), typeof(GenericTest<,>)) 相似这样的 Open generic 怎么支持呢?

其实对于泛型,咱们经过 emit 生成泛型类型一点问题都没有,惟一的难点是很差生成 Get<T>() 这样的方法调用, 由于IL须要反射找到的具体方法,好比Get<int>() Get<bool>() 等等,不能是不明确的 Get<T>()

要解决这个问题就只能将实际的调用延迟到运行时调用再生成具体的调用,伪码大体以下:

interface GenericTest<T,R>
{
    T Get<T>(T i) => i;
}

class GenericTestProxy<T,R> : GenericTest<T,R>
{
    T Get<T>(T i) => this.GetType().GetMethod("Get<T>").Invoke(i);
}
相关文章
相关标签/搜索