首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

.NET静态代码编织——肉夹馍(Rougamo)4.0

编程知识
2024年08月12日 07:07

肉夹馍(https://github.com/inversionhourglass/Rougamo),一款编译时AOP组件。相比动态代理AOP需要在应用启动时进行初始化,编译时完成代码编织的肉夹馍减少了应用启动初始化的时间,同时肉夹馍还支持所有种类的方法,无论方法是同步还是异步、静态还是实例、构造方法还是属性都是支持的。肉夹馍无需初始化,编写好切面类型后直接应用到对应方法上即可,同时肉夹馍还提供了方法特征匹配和类AspectJ表达式匹配的批量应用规则。

异步切面

得益于3.0对切面实现方式的改变,4.0版本基于代理织入实现了异步切面功能。那么什么是异步切面呢?直白的说就是新增了OnEntry/OnSuccess/OnException/OnExit对应的异步方法OnEntryAsync/OnSuccessAsync/OnExceptionAsync/OnExitAsync

如何使用

要编写异步切面,一般继承AsyncMoAttributeAsyncMo,然后重写OnXxxAsync方法即可。

// 定义切面类型
public class TestAttribute : AsyncMoAttribute
{
    public override async ValueTask OnEntryAsync(MethodContext) { }
    
    public override async ValueTask OnSuccessAsync(MethodContext) { }
    
    public override async ValueTask OnExceptionAsync(MethodContext) { }
    
    public override async ValueTask OnExitAsync(MethodContext) { }
}

public class Cls
{
    // 应用到同步方法上
    [Test]
    public void M() { }

    // 应用到异步方法上
    [Test]
    public static async Task MAsync() => Task.Yield();
}

聊聊细节

在了解的异步切面的使用方式后你可能有一个疑问:同步切面在异步方法上的表现和异步切面在同步方法上的表现是什么样的?回答这个问题前,我们可以先看一下AsyncMoAttribute的源码:

public abstract class AsyncMoAttribute : RawMoAttribute
{
    public override ValueTask OnEntryAsync(MethodContext context) => default;

    public override ValueTask OnExceptionAsync(MethodContext context) => default;

    public override ValueTask OnSuccessAsync(MethodContext context) => default;

    public override ValueTask OnExitAsync(MethodContext context) => default;

    public sealed override void OnEntry(MethodContext context)
    {
        OnEntryAsync(context).ConfigureAwait(false).GetAwaiter().GetResult();
    }

    public sealed override void OnException(MethodContext context)
    {
        OnExceptionAsync(context).ConfigureAwait(false).GetAwaiter().GetResult();
    }

    public sealed override void OnSuccess(MethodContext context)
    {
        OnSuccessAsync(context).ConfigureAwait(false).GetAwaiter().GetResult();
    }

    public sealed override void OnExit(MethodContext context)
    {
        OnExitAsync(context).ConfigureAwait(false).GetAwaiter().GetResult();
    }
}

从源码中可以看到,AsyncMoAttribute是包含同步切面方法的,同时还有默认实现,实现代码就是直接调用异步切面方法,然后GetResult。同样的,如果你去看MoAttribute的源码,你就会发现,MoAttribute同样拥有异步切面方法,并且默认实现就是调用同步切面方法。所以,关于上面那个问题的答案就是:在同步方法中将调用同步切面方法,在异步方法中将调用异步切面方法。

此时你可能会有另一个疑问:既然AsyncMoAttributeMoAttribute都拥有全部的同步切面方法和异步切面方法没什么还要分两个类呢?

这是综合便捷性和安全性考虑后的设计。正如前面所说,肉夹馍会在同步方法中将调用同步切面方法,在异步方法中将调用异步切面方法。如果不分开为两个类继续使用MoAttribute,那么首先一个问题:MoAttribute中的所有切面方法是应该设计为抽象方法让子类实现全部同步异步切面方法,还是设计为带有默认实现的虚方法让子类自由选择重写方法?

  • 选择设计为抽象方法
    设计为抽象方法就增加了子类在继承时的额外工作,需要实现所有的切面方法。

  • 选择设计为带有默认实现的虚方法
    选择这种方法就面临另一个问题:默认实现采用空方法实现,还是采用异步切面与同步切面的互调用(在异步切面方法中默认调用同步切面方法,在同步切面方法中默认调用异步切面方法)

    • 采用空方法实现
      由于是虚方法,所以子类在继承MoAttribute时并不是必须重写虚方法,所以如果重写了某个同步切面方法但是没有重写对应的异步切面方法,那么就会导致该切面类型在应用到同步方法上和异步方法上会有不同的表现,这往往是不符合预期的。
    • 采用异步切面与同步切面的互调用
      和采用空方法实现存在同样的问题,但后果却更严重。比如如果在继承MoAttribute时因为不需要在方法退出时做任何操作,所以既没有重写OnExit也没有重写OnExitAsync,那么在方法退出时调用OnExitOnExitAsync时就会出现OnExitOnExitAsync递归调用。

从上面的说明,你应该能理解将同步切面和异步切面分为两个类型的原因了。如果你观察细致,你可能已经发现上面AsyncMoAttribute源码中的同步切面方法还增加了sealed关键字,这也是为了增加安全性,禁止重写,避免在重写方法时因IDE智能提示重写了同步切面方法而又没有重写对应的异步方法,导致出现同步切面方法和异步切面方法表现不一致的问题。

完全自定义的RawMoAttribute

既然MoAttributeAsyncMoAttribute的默认实现是直接调用对应的方法,那么如果我觉得默认的实现不是最优呢,比如AsyncMoAttribute默认的同步切面是直接调用异步切面然后同步等待完成,我有更好的同步方案,应该怎么做呢,毕竟同步切面方法都通过sealed关键字禁止重写了。

细心的你在查看上面AsyncMoAttribute源码时可能已经发现了,AsyncMoAttribute继承自RawMoAttribute,同样的MoAttribute也继承自RawMoAttributeRawMoAttribute开放了所有同步异步切面方法,这些方法都是抽象方法,你可以完全自定义同步异步切面。在继承RawMoAttribute实现同步异步切面方法时需要注意前面提到的:避免同步切面和异步切面的代码逻辑有差异,避免同步切面方法和异步切面方法出现递归调用。

其他更新内容

新增类型

除了上面提到的AsyncMoAttributeRawMoAttribute,还新增了RawMO,MO,AsyncMo分别与RawMoAttribute,MoAttribute,AsyncMoAttribute对应,区别在于后者继承自Attribute。因为肉夹馍的应用方式除了Attribute应用,还可以通过 实现空接口IRougamo<> 的方式来应用,这种方式并不需要类型是Attribute子类,当然Attribute子类也是接受的,这里只是提供了不继承Attribute的选择。

性能优化之强制同步

在介绍异步切面时有说到:同步方法会调用同步切面,异步方法会调用异步切面。这一设定在默认情况是很好的设定,但如果你的切面操作完全不涉及异步操作,那么在异步方法中实际并不需要调用异步切面,因为异步切面走了一层ValueTask包装,相比同步切面会存在额外的开销。在这种情况下,可以通过ForceSync属性设置在异步方法中需要强制执行同步切面的方法:

public class TestAttribute : MoAttribute
{
    // 在异步方法中,OnEntry和OnExit将强制调用同步切面方法
    public override ForceSync ForceSync => ForceSync.OnEntry | ForceSync.OnExit;
}

其实,如果对性能要求并不是那么严格,是可以不去设置ForceSync的,肉夹馍已采用ValueTask,默认的异步切面方法对同步切面方法包装的额外开销十分有限。

async void的特别说明

在3.0发布时便有聊到,3.0后采用的代理织入方式对async void的支持可能与你的预期效果不同。具体请跳转查看 async void特别说明

考虑到async void是不推荐的使用方式(官方也不推荐),所以决定不对async void做更多的适配工作,继续沿用3.0的做法,将async void方法当做普通的void返回值的同步方法看待。但与3.0不同的是,在4.0版本中如果发现async void方法上应用了肉夹馍切面类型,将在编译时产生一个MSBuild告警。告警信息往往不容易引起注意,如果你确定自己并不希望async void上应用肉夹馍切面类型,或者你希望子出现这种情况时能提醒你让你做相应的修改,那么你可以在项目文件的PropertyGroup节点下新增一个子节点<FodyTreatWarningsAsErrors>true</FodyTreatWarningsAsErrors>,这个配置会让Fody产生的告警信息变为错误信息,从而使得编译失败达到提醒的目的。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <FodyTreatWarningsAsErrors>true</FodyTreatWarningsAsErrors>
  </PropertyGroup>
</Project>

MethodContext成员变化

删除MethodContext中的IsAsync, IsIterator, MosNonEntryFIFO, Data属性,将RealReturnType标记为过时并隐藏,同时新增TaskReturnType属性,该属性与RealReturnType具有类似功能。

配置文件智能提示

肉夹馍有些许可配置项,这些配置项可在FodyWeavers.xml中配置,详见 配置项。现在为这些配置增加了对应的xml schema,在修改FodyWeavers.xmlRougamo节点时会出现智能提示。

From:https://www.cnblogs.com/nigture/p/18353032
本文地址: http://shuzixingkong.net/article/1006
0评论
提交 加载更多评论
其他文章 Java学习笔记2--JDK的安装和配置
一.进入oracle官网,下载jdk oracle官网:Oracle | Cloud Applications and Cloud Platform ps:不同的浏览器,可能进入oracle官网,会只显示部分内容,所以建议使用google Chrome浏览器 在下载之前,首先需要去查看本机电脑的配置
Java学习笔记2--JDK的安装和配置 Java学习笔记2--JDK的安装和配置 Java学习笔记2--JDK的安装和配置
《熬夜整理》保姆级系列教程-玩转Wireshark抓包神器教程(3)-Wireshark在MacOS系统上安装部署
1.简介 上一篇中介绍和讲解、分享了Wireshark在Windows系统上安装部署,今天就介绍和讲解、分享Wireshark在MacOS系统上安装部署。Wireshark不仅是Windows系统网络协议分析软件也是一款mac网络协议分析软件,任何负责的网络分析人员都对这个软件情有独钟。如今,几乎没
《熬夜整理》保姆级系列教程-玩转Wireshark抓包神器教程(3)-Wireshark在MacOS系统上安装部署 《熬夜整理》保姆级系列教程-玩转Wireshark抓包神器教程(3)-Wireshark在MacOS系统上安装部署 《熬夜整理》保姆级系列教程-玩转Wireshark抓包神器教程(3)-Wireshark在MacOS系统上安装部署
当 Spring 循环依赖碰上 Aysnc,调试过程中出现 BeanCurrentlyInCreationException,有点意思
开心一刻 前两天有个女生加我,我同意了 第一天,她和我聊文学,聊理想,聊篮球,聊小猫小狗 第二天,她和我说要看我腹肌 吓我一跳,我反手就删除拉黑,我特喵一肚子的肥肉,哪来的腹肌! 循环依赖 关于 Spring 的循环依赖,我已经写了 4 篇 Spring 的循环依赖,源码详细分析 → 真的非要三级缓
当 Spring 循环依赖碰上 Aysnc,调试过程中出现 BeanCurrentlyInCreationException,有点意思 当 Spring 循环依赖碰上 Aysnc,调试过程中出现 BeanCurrentlyInCreationException,有点意思 当 Spring 循环依赖碰上 Aysnc,调试过程中出现 BeanCurrentlyInCreationException,有点意思
Blazor开发框架Known-V2.0.7
V2.0.7 Known是基于Blazor的企业级快速开发框架,低代码,跨平台,开箱即用,一处代码,多处运行。 官网:http://known.pumantech.com Gitee: https://gitee.com/known/Known Github:https://github.com/k
Blazor开发框架Known-V2.0.7 Blazor开发框架Known-V2.0.7 Blazor开发框架Known-V2.0.7
C#窗体自定义快捷操作键的实现 - 开源研究系列文章
这次想到应用程序的窗体的快捷操作键的使用的问题。 上次发布过一个快捷键的例子(https://www.cnblogs.com/lzhdim/p/18342051),区别在于它是操作系统全局注册的热键,如果其它应用程序注册了对应的热键,那就会失效。此例子是对某个窗体里的按键的操作进行的快捷键的操作,有
C#窗体自定义快捷操作键的实现 - 开源研究系列文章 C#窗体自定义快捷操作键的实现 - 开源研究系列文章 C#窗体自定义快捷操作键的实现 - 开源研究系列文章
深入理解 PHP 高性能框架 Workerman 守护进程原理
守护进程顾名思义就是能够在后台一直运行的进程,不会霸占用户的会话终端,脱离了终端的控制。相信朋友们对这东西都不陌生了吧?如果连这个概念都还不能理解的话,建议回炉重造多看看 Linux 进程管理相关的基础知识。
深入理解 PHP 高性能框架 Workerman 守护进程原理
(七)Redis 持久化 AOF、RDB
Redis 一旦服务器宕机,内存中的数据将全部丢失,从后端数据库恢复这些数据,对数据库压力很大,且性能肯定比不上从 Redis 中读取,会拖慢应用程序。所以,对 Redis 来说,实现数据的 持久化 ,避免从后端数据库中进行恢复,是至关重要的。 1、AOF 日志 AOF 日志是先执行命令,把数据写入
(七)Redis 持久化 AOF、RDB (七)Redis 持久化 AOF、RDB (七)Redis 持久化 AOF、RDB
观存储历史,论数据未来
在探索未来的数据存储技术时,我们不禁感慨于人类智慧的无限可能。从古代的竹简、木简,到蔡伦的造纸术,再到现代的机械硬盘、固态硬盘,直至云存储和前沿的DNA存储与量子存储,技术的演进总是令人惊叹。历史告诉我们,在时间面前,所有不可逾越的技术壁垒都是纸老虎。对于程序员而言,尽管底层存储介质的更迭可能对我们
观存储历史,论数据未来 观存储历史,论数据未来 观存储历史,论数据未来