.NET静态代码编织——肉夹馍(Rougamo)4.0
肉夹馍(https://github.com/inversionhourglass/Rougamo),一款编译时AOP组件。相比动态代理AOP需要在应用启动时进行初始化,编译时完成代码编织的肉夹馍减少了应用启动初始化的时间,同时肉夹馍还支持所有种类的方法,无论方法是同步还是异步、静态还是实例、构造方法还是属性都是支持的。肉夹馍无需初始化,编写好切面类型后直接应用到对应方法上即可,同时肉夹馍还提供了方法特征匹配和类AspectJ表达式匹配的批量应用规则。
异步切面
得益于3.0对切面实现方式的改变,4.0版本基于代理织入实现了异步切面功能。那么什么是异步切面呢?直白的说就是新增了OnEntry/OnSuccess/OnException/OnExit
对应的异步方法OnEntryAsync/OnSuccessAsync/OnExceptionAsync/OnExitAsync
。
如何使用
要编写异步切面,一般继承AsyncMoAttribute
或AsyncMo
,然后重写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
同样拥有异步切面方法,并且默认实现就是调用同步切面方法。所以,关于上面那个问题的答案就是:在同步方法中将调用同步切面方法,在异步方法中将调用异步切面方法。
此时你可能会有另一个疑问:既然AsyncMoAttribute
和MoAttribute
都拥有全部的同步切面方法和异步切面方法没什么还要分两个类呢?
这是综合便捷性和安全性考虑后的设计。正如前面所说,肉夹馍会在同步方法中将调用同步切面方法,在异步方法中将调用异步切面方法。如果不分开为两个类继续使用MoAttribute
,那么首先一个问题:MoAttribute
中的所有切面方法是应该设计为抽象方法让子类实现全部同步异步切面方法,还是设计为带有默认实现的虚方法让子类自由选择重写方法?
选择设计为抽象方法
设计为抽象方法就增加了子类在继承时的额外工作,需要实现所有的切面方法。选择设计为带有默认实现的虚方法
选择这种方法就面临另一个问题:默认实现采用空方法实现,还是采用异步切面与同步切面的互调用(在异步切面方法中默认调用同步切面方法,在同步切面方法中默认调用异步切面方法)- 采用空方法实现
由于是虚方法,所以子类在继承MoAttribute
时并不是必须重写虚方法,所以如果重写了某个同步切面方法但是没有重写对应的异步切面方法,那么就会导致该切面类型在应用到同步方法上和异步方法上会有不同的表现,这往往是不符合预期的。 - 采用异步切面与同步切面的互调用
和采用空方法实现存在同样的问题,但后果却更严重。比如如果在继承MoAttribute
时因为不需要在方法退出时做任何操作,所以既没有重写OnExit
也没有重写OnExitAsync
,那么在方法退出时调用OnExit
或OnExitAsync
时就会出现OnExit
和OnExitAsync
递归调用。
- 采用空方法实现
从上面的说明,你应该能理解将同步切面和异步切面分为两个类型的原因了。如果你观察细致,你可能已经发现上面AsyncMoAttribute
源码中的同步切面方法还增加了sealed
关键字,这也是为了增加安全性,禁止重写,避免在重写方法时因IDE智能提示重写了同步切面方法而又没有重写对应的异步方法,导致出现同步切面方法和异步切面方法表现不一致的问题。
完全自定义的RawMoAttribute
既然MoAttribute
和AsyncMoAttribute
的默认实现是直接调用对应的方法,那么如果我觉得默认的实现不是最优呢,比如AsyncMoAttribute
默认的同步切面是直接调用异步切面然后同步等待完成,我有更好的同步方案,应该怎么做呢,毕竟同步切面方法都通过sealed
关键字禁止重写了。
细心的你在查看上面AsyncMoAttribute
源码时可能已经发现了,AsyncMoAttribute
继承自RawMoAttribute
,同样的MoAttribute
也继承自RawMoAttribute
。RawMoAttribute
开放了所有同步异步切面方法,这些方法都是抽象方法,你可以完全自定义同步异步切面。在继承RawMoAttribute
实现同步异步切面方法时需要注意前面提到的:避免同步切面和异步切面的代码逻辑有差异,避免同步切面方法和异步切面方法出现递归调用。
其他更新内容
新增类型
除了上面提到的AsyncMoAttribute
和RawMoAttribute
,还新增了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.xml
的Rougamo
节点时会出现智能提示。
.NET静态代码编织——肉夹馍(Rougamo)4.0的更多相关文章
- .NET静态代码织入——肉夹馍(Rougamo) 发布1.1.0
肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应 ...
- .NET静态代码织入——肉夹馍(Rougamo) 发布1.2.0
肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应 ...
- .NET静态代码织入——肉夹馍(Rougamo) 发布1.4.0
肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应 ...
- .NET静态代码织入——肉夹馍(Rougamo)
肉夹馍是什么 肉夹馍通过静态代码织入方式实现AOP的组件..NET常用的AOP有Castle DynamicProxy.AspectCore等,以上两种AOP组件都是通过运行时生成一个代理类执行AOP ...
- java子父类初始化顺序 (1)父类静态代码块(2)父类静态变量初始化(3)子类静态代码块(4)子类静态变量初始化(5)main(6)有对象开辟空间都为0(7)父类显示初始化(8)父类构造(9)子类显示初始化(10)子类构造
标题 静态代码块与静态成员变量还要看代码的先后顺序 看程序,说出结果 结果为: x=0 看程序,说出结果 结果如下: 补充 : 静态代码块:static{ } 在JVM加载时即执行,先于主方法执行,用 ...
- pmd静态代码分析
在正式进入测试之前,进行一定的静态代码分析及code review对代码质量及系统提高是有帮助的,以上为数据证明 Pmd 它是一个基于静态规则集的Java源码分析器,它可以识别出潜在的如下问题:– 可 ...
- JAVA语言搭建白盒静态代码、黑盒网站插件式自动化安全审计平台
近期打算做一个插件化的白盒静态代码安全审计自动化平台和黑盒网站安全审计自动化平台.现在开源或半开源做黑盒网站安全扫描的平台,大多是基于python脚本,安全人员贡献python脚本插件增强平台功能.对 ...
- Java代码执行顺序(静态变量,非静态变量,静态代码块,代码块,构造函数)加载顺序
//据说这是一道阿里巴巴面试题,先以这道题为例分析下 public class Text { public static int k = 0; public static Text t1 = new ...
- Java提高篇——静态代码块、构造代码块、构造函数以及Java类初始化顺序
静态代码块:用staitc声明,jvm加载类时执行,仅执行一次构造代码块:类中直接用{}定义,每一次创建对象时执行.执行顺序优先级:静态块,main(),构造块,构造方法. 构造函数 public H ...
- 常用 Java 静态代码分析工具的分析与比较
常用 Java 静态代码分析工具的分析与比较 简介: 本文首先介绍了静态代码分析的基 本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBu ...
随机推荐
- 基于Vue+OpenSeaDragon的数字细胞阅片开发
前端框架: vue+elementui+openseadragon 后端 .net core 5.0 数据库 mysql 目前项目效果如图 在原有的基础上新增了阅片轨迹 图像调节,绘图,截图等功能. ...
- Asp.net Core 经过nginx代理后获取不到真实ip和scheme的问题
背景 我最近在一个Asp.net core Web 程序在经过nginx代理后 ,总是获取不到用户真实i和scheme(HttpContext.Request.Scheme),挠头: 我们一般从请求头 ...
- 记一个,生产遇到的redission锁,释放问题:lock.tryLock(0, 0, TimeUnit.SECONDS)
package com.aswatson.cdc.test; import org.redisson.Redisson; import org.redisson.api.RLock; import o ...
- Android系统启动:2-Init篇
Android系统启动:Init篇 原文:http://gityuan.com/2016/02/05/android-init/ 概述 init进程是Linux系统中用户空间的第一个进程,进程号固定为 ...
- EEPROM、Flash有关知识
存储器分为两大类:RAM(Random Access Memory,任意地址访问储存器)和ROM(Read-Only Memory,只读储存器). 发展历史 ROM Read-Only Memory, ...
- NXP i.MX 6ULL工业开发板规格书( ARM Cortex-A7,主频792MHz)
1 评估板简介 创龙科技TLIMX6U-EVM是一款基于NXP i.MX 6ULL的ARM Cortex-A7高性能低功耗处理器设计的评估板,由核心板和评估底板组成.核心板经过专业的PCB Layou ...
- Mac Mysql初始化密码
初始化密码 step1 苹果->系统偏好设置->最下面一行上点击mysql图标, 在弹出页面中 关闭mysql服务(点击stop mysql server) step2 登录终端:comm ...
- [oeasy]python0128_unicode_字符集_character_set_八卦_星座
unicode 回忆上次内容 中国的简体和繁体汉字 字符数量都超级大 彼此还认对方为乱码 如果有一种编码所有的字符都能编进去就好了 中日韩(CJK) 欧洲拼音 梵文 阿拉伯文 卢恩字符 等等等都包 ...
- 【VMware VCF】VMware Cloud Foundation Part 04:准备 ESXi 主机。
VMware Cloud Foundation 管理域部署要求至少准备 4 台 ESXi 主机作为最小计算单元,如果采用整合部署(管理域和 VI 工作负载域合并),还需要根据实际情况适量增加 ESXi ...
- java面试一日一题:mysql的索引下推了解吗
问题:请问你了解索引下推吗 分析:该问题主要考察对mysql优化方面的理解 回答要点: 主要从以下几点去考虑, 1.mysql中索引的概念? 2.索引下推的理解及意义? 在面试过程中问到mysql,必 ...