Norns.Urd 是什么?

Norns.Urd 是一个基于emit实现动态代理的轻量级AOP框架.

版本基于 netstandard2.0. 所以哪些.net 版本能用你懂的。

完成这个框架的目的主要出自于个人以下意愿:

  • 静态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.Urd

对了,如果不了解AOP的同学,可以看看这些文章:

面向切面的程序设计

什么是面向切面编程AOP?

AOP 有几种实现方式?

Norns.Urd 中的一些设计

Norns.Urd的实现前提

由于Norns.Urd的实现基于以下两点前提

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

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

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

如何设计解决的?

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

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

以前接触一些其他aop实现框架,很多都需要将拦截代码分为 方法前 / 方法后 / 有异常等等,个人觉得这样的形式还是一定程度上影响拦截器实现的代码思路,总觉得不够顺滑

但是像 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);
}
}

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

Norns.Urd 中的一些设计的更多相关文章

  1. 介绍一个新库: Norns.Urd.HttpClient

    Norns.Urd.HttpClient Norns.Urd.HttpClient 基于AOP框架 Norns.Urd实现, 是对 System.Net.Http下的 HttpClient封装,让大家 ...

  2. 看懂此文,不再困惑于 JS 中的事件设计

    看懂此文,不再困惑于 JS 中的事件设计 今天刚在关注的微信公众号看到的文章,关于JS事件的,写的很详细也很容易理解,相关的知识点都有总结到,看完就有种很舒畅的感觉,该串起来的知识点都串起来了.反正一 ...

  3. JS 中的事件设计

    看懂此文,不再困惑于 JS 中的事件设计 原文出处: aitangyong    抽空学习了下javascript和jquery的事件设计,收获颇大,总结此贴,和大家分享. (一)事件绑定的几种方式 ...

  4. 推荐一款基于Angular实现的企业级中后台前端/设计解决方案脚手架

    ng-alain 是一个企业级中后台前端/设计解决方案脚手架,我们秉承 Ant Design 的设计价值观,目标也非常简单,希望在Angular上面开发企业后台更简单.更快速.随着『设计者』的不断反馈 ...

  5. Ceph中Bufferlist的设计与使用

    转自:https://www.ustack.com/blog/bufferlist/ 如果非要在整个Ceph中,找出一个类最重要,我觉得非Bufferlist莫属了,原因很简单,因为Bufferlis ...

  6. mysql中数据库的设计

      软件开发流程(CMMI): 1):项目启动; 2):项目计划: 3):需求分析; 需要得到的结果是什么? 4):系统设计;         该怎么做? 5):系统开发; 6):系统测试; 7):系 ...

  7. 在Eclipse中使用WindowBuilder设计Swing程序

    在Eclipse中使用WindowBuilder设计Swing程序     Swing程序表示Java的客户端窗体程序,除了通过手动编写代码的方式设计Swing程序之外,Eclipse中还提供了一种W ...

  8. Java实现图形界面的三部曲及IDE中的窗口设计

    设计和实现图形用户界面的工作主要有以下几点: • (1)创建组件(Component) • 创建组成界面的各种元素,如按钮.文本框等.• (2)指定布局(Layout) • 根据具体需要排列它们的位置 ...

  9. java中HashMap的设计精妙在哪?

    摘要:本文结合图解和问题,教你一次性搞定HashMap 本文分享自华为云社区<java中HashMap的设计精妙在哪?用图解和几个问题教你一次性搞定HashMap>,作者:breakDaw ...

随机推荐

  1. Linux 网络栈 转载

    此文章  来自      http://arthurchiao.art/blog/tuning-stack-rx-zh/ [译] Linux 网络栈监控和调优:接收数据(2016) Published ...

  2. mysql权限管理命令

    #创建用户 create user 'songwp' IDENTIFIED BY '1234' #用户授权 GRANT ALL ON DB01.* TO 'songwp' #撤销权限 REVOKE A ...

  3. Fiddler的一系列学习瞎记(没有章法的笔记)

    前言: 工作上要接触很多移动设备,进行测试,所以抓包软件不能少,但是看你习惯,你要是说我喜欢charles,也可以,毕竟我也买不起苹果电脑,就不拿charles装在windows上了,还是乖乖的Fid ...

  4. 【译】Arc 在 Rust 中是如何工作的

    原文标题:How Arc works in Rust 原文链接:https://medium.com/@DylanKerler1/how-arc-works-in-rust-b06192acd0a6 ...

  5. 在Linux中输入命令时打错并按了enter

    今天在Linux中输入命令时,打错一个单词了,之后出现一串串的~,按ESC也没用, 并在底部出现:quit<enter> to exit vim 解决办法: 按几下 esc 确保 vim ...

  6. [COCI2016-2017#1] Mag

    [COCI2016-2017#1] Mag 题解 题目TP门 题目描述 你将获得一棵由无向边连接的树.树上每个节点都有一个魔力值. 我们定义,一条路径的魔力值为路径上所有节点魔力值的乘积除以路径上的节 ...

  7. 红外遥控接收发射原理及ESP8266实现

    红外遥控是利用近红外光进行数据传输的一种控制方式.近红外光波长0.76um~1.5um ,红外遥控收发器件波长一般为 0.8um~0.94um ,具有传输效率高,成本低,电路实现简单,抗干扰强等特点, ...

  8. python3 Failed to establish a new connection: [WinError 10061] 由于目标计算机积极拒绝,无法连接

    报错源代码from selenium import webdriverimport unittestimport timefrom HTMLTestRunner import HTMLTestRunn ...

  9. Eclipse中get/set方法自动生成

    代码中点击右键(快捷键Ctrl+Alt+S) ->Source ->Generate Getters and Setters... ->全选(或选择需要生成的字段/方法) 动图: 静 ...

  10. [Android systrace系列] systrace入门第一式

    转载请注明出处:https://www.cnblogs.com/zzcperf/p/13978915.html Android systrace是分析性能问题最称手的工具之一,可以提供丰富的手机运行信 ...