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); }