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. vite 搭建Vue3.0项目

    1.全局安装vite:npm install create-vite-app -g 2.创建项目:npx create-vite-app project-name 3.cd project-name ...

  2. nginx&http 第三章 ngx 事件event epoll 处理

    1. epoll模块命令集 ngx_epoll_commands  epoll模块上下文 ngx_epoll_module_ctx  epoll模块配置 ngx_epoll_module static ...

  3. golang 简单工厂模式

    package kit //golang简单工厂模式 //go 语言没有构造函数一说,所以一般会定义NewXXX函数来初始化相关类. NewXXX 函数返回接口时就是简单工厂模式,也就是说Golang ...

  4. shell编程之字符串操作

    shell中字符串操作主要有以下几种,其中:pattern ,old中可以使用通配符: ${#var} :返回字符串变量var的长度 ${var:m} :返回${var}中从第m+1个字符到最后的部分 ...

  5. TCP特点

    1.基于字节流:面向连接:可靠传输:缓冲传输:全双工:流量控制.TCP如何保证可靠性:差错:校验和丢包:超时重传+确认失序:seq(序号)重复:seq(序号)1.数据被分割成TCP认为最合适发送的数据 ...

  6. UNP——第二章,常见协议概述

    1.为什么要了解协议 程序员与协议合作,完成应用. 了解协议是为了了解协议完成了什么,提供了什么服务,自己还应该做什么. 2.从协议的角度,套接字是什么 套接字是协议的接口, IP套接字,代表可使用I ...

  7. 07 . 前端工程化(ES6模块化和webpack打包)

    模块化规范 传统开发模式主要问题 /* 1. 命名冲突 2. 文件依赖 */ 通过模块化解决上述问题 /* 模块化就是把单独的一个功能封装在一个模块(文件)中,模块之间相互隔离, 但是可以通过特定的接 ...

  8. mybatis-plus使用p6spy 插件进行sql性能分析

    1.依赖 <dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId& ...

  9. day96:flask:flask-migrate&flask-session&蓝图Blueprint&蓝图的运行机制

    目录 1.flask-migrate 2.flask-session 3.蓝图:Blueprint 4.蓝图的运行机制 1.数据库迁移:flask-migrate 1.Flask的数据库迁移 在开发过 ...

  10. FL Slayer合成器功能之顶部组件介绍

    本章节采用图文结合的方式给大家介绍电音编曲软件--FL Studio中的插件FL Slayer合成器中的顶部组件,它是电吉他模拟合成器,感兴趣的朋友可以一起沟通学习交流. FL Slayer(杀手)合 ...