Norns.Urd 中的一些设计
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的实现基于以下两点前提
将 sync 和 async 方法同时兼容且如何将实现选择权完全交予用户
- 其实这点还好,工作量变成两倍多一些就好,sync 和 async 完全拆分成两套实现。
- 提供给用户的Interceptor接口要提供 sync 和 async 混合在一套实现代码的方案,毕竟不能强迫用户实现两套代码,很多场景用户不需要为sync 和 async 的差异而实现两套代码
不包含任何内置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 中的一些设计的更多相关文章
- 介绍一个新库: Norns.Urd.HttpClient
Norns.Urd.HttpClient Norns.Urd.HttpClient 基于AOP框架 Norns.Urd实现, 是对 System.Net.Http下的 HttpClient封装,让大家 ...
- 看懂此文,不再困惑于 JS 中的事件设计
看懂此文,不再困惑于 JS 中的事件设计 今天刚在关注的微信公众号看到的文章,关于JS事件的,写的很详细也很容易理解,相关的知识点都有总结到,看完就有种很舒畅的感觉,该串起来的知识点都串起来了.反正一 ...
- JS 中的事件设计
看懂此文,不再困惑于 JS 中的事件设计 原文出处: aitangyong 抽空学习了下javascript和jquery的事件设计,收获颇大,总结此贴,和大家分享. (一)事件绑定的几种方式 ...
- 推荐一款基于Angular实现的企业级中后台前端/设计解决方案脚手架
ng-alain 是一个企业级中后台前端/设计解决方案脚手架,我们秉承 Ant Design 的设计价值观,目标也非常简单,希望在Angular上面开发企业后台更简单.更快速.随着『设计者』的不断反馈 ...
- Ceph中Bufferlist的设计与使用
转自:https://www.ustack.com/blog/bufferlist/ 如果非要在整个Ceph中,找出一个类最重要,我觉得非Bufferlist莫属了,原因很简单,因为Bufferlis ...
- mysql中数据库的设计
软件开发流程(CMMI): 1):项目启动; 2):项目计划: 3):需求分析; 需要得到的结果是什么? 4):系统设计; 该怎么做? 5):系统开发; 6):系统测试; 7):系 ...
- 在Eclipse中使用WindowBuilder设计Swing程序
在Eclipse中使用WindowBuilder设计Swing程序 Swing程序表示Java的客户端窗体程序,除了通过手动编写代码的方式设计Swing程序之外,Eclipse中还提供了一种W ...
- Java实现图形界面的三部曲及IDE中的窗口设计
设计和实现图形用户界面的工作主要有以下几点: • (1)创建组件(Component) • 创建组成界面的各种元素,如按钮.文本框等.• (2)指定布局(Layout) • 根据具体需要排列它们的位置 ...
- java中HashMap的设计精妙在哪?
摘要:本文结合图解和问题,教你一次性搞定HashMap 本文分享自华为云社区<java中HashMap的设计精妙在哪?用图解和几个问题教你一次性搞定HashMap>,作者:breakDaw ...
随机推荐
- Linux杂谈: 实现一种简单实用的线程池(C语言)
基本功能 1. 实现一个线程的队列,队列中的线程启动后不再释放: 2. 没有任务执行时,线程处于pending状态,等待唤醒,不占cpu: 3. 当有任务需要执行时,从线程队列中取出一个线程执行任务: ...
- 对pipe downstream的思考&分析
回到ngx_http_upstream_send_response,如果是buffering,就会进入后面的处理过程,准备一个ngx_event_pipe_t结构的数据,这个结构可以通过upst ...
- 从ceph对象中提取RBD中的指定文件
前言 之前有个想法,是不是有办法找到rbd中的文件与对象的关系,想了很久但是一直觉得文件系统比较复杂,在fs 层的东西对ceph来说是透明的,并且对象大小是4M,而文件很小,可能在fs层进行了合并,应 ...
- 利用HUtool读取Excel内容
// 1.获取上传文件输入流 InputStream inputStream = null; try{ inputStream = file.getInputStream(); }catch (Exc ...
- Python_教程_使用Visual Studio Code开发Django项目
如何获得 Visual Studio Code 访问 http://code.visualstudio.com 下载并安装. 前提条件 安装Python 2.7 及 Python 3.5,Window ...
- 学习笔记:[算法分析]数据结构与算法Python版[基本的数据结构-上]
线性结构Linear Structure ❖线性结构是一种有序数据项的集合,其中 每个数据项都有唯一的前驱和后继 除了第一个没有前驱,最后一个没有后继 新的数据项加入到数据集中时,只会加入到原有 某个 ...
- kali 系列学习06 - 攻击之密码破解
参考书 <kali linux渗透测试技术详解> 清华大学出版社 2015 杨波 一.medusa使用 原理:并行登陆破解 1.只输入单个命令,是查看帮助 root@kali:/tool ...
- bWAPP----HTML OS Command Injection - Blind
OS Command Injection - Blind 先上代码,他判断了win还是linux然后进行了ping但是结果并没有返回. 1 <div id="main"> ...
- addslashes()
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串. 作用:防止sql注入
- 已安装的nginx添加其他模块
总体操作就是添加新模块并重新编译源码,然后把编译后的nginx可执行文件覆盖原来的那个即可.1 查看已安装的参数nginx -V拷贝那些巴拉巴拉的参数,后面编译的时候使用 2 下载相同版本号的源码,解 ...