Norns.Urd 是一个基于emit实现动态代理的轻量级AOP框架.
版本基于 netstandard2.0. 因此哪些.net 版本能用你懂的。
完成这个框架的目的主要出自于我的如下意愿:html
但愿该库能对你们有些小小的做用
中文文档在:https://fs7744.github.io/Norns.Urd/zh-cn/index.html
顺便求个star, github 地址:https://github.com/fs7744/Norns.Urdgit
对了,若是不了解AOP的同窗,能够看看这些文章:
面向切面的程序设计
什么是面向切面编程AOP?
AOP 有几种实现方式?github
因为Norns.Urd的实现基于如下两点前提编程
将 sync 和 async 方法同时兼容且如何将实现选择权彻底交予用户app
不包含任何内置DI,但要总体都为支持DI而做框架
目前方案不必定完美,暂时算解决了问题而已 (有更好方案请必定要告诉我,我迫切须要学习)异步
之前接触一些其余aop实现框架,不少都须要将拦截代码分为 方法前 / 方法后 / 有异常等等,我的以为这样的形式仍是必定程度上影响拦截器实现的代码思路,总以为不够顺滑async
可是像 ASP.NET Core Middleware就感受很是不错,以下图和代码:函数
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); } }
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框架都有注册类型,咱们能够经过 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); }