.NET静态代码织入——肉夹馍(Rougamo)发布3.0
肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应用启动的初始化时间让服务更快可用,同时还能对静态方法进行AOP操作。
正文
虽又是一个大版本,但本次大版本没有重大的功能上线,主要是修改了代码织入方式,这样的改动牵扯到一些现有功能。
代码织入方式变化
在3.0之前的版本中采用的是代码内嵌的方式进行织入,下面用简化代码进行演示:
// 原始方法
public void M()
{
Console.WriteLine(1);
Console.WriteLine(2);
Console.WriteLine(3);
}
// 3.0版本之前织入代码后
public void M()
{
var context = new MethodContext(...);
var mo = new AbcAttribute();
mo.OnEntry(context);
try
{
Console.WriteLine(1);
Console.WriteLine(2);
Console.WriteLine(3);
mo.OnSuccess(context);
}
catch (Exception e)
{
context.Exception = e;
mo.OnException(context);
throw;
}
finally
{
mo.OnExit(context);
}
}
在3.0版本中采用的是代理调用的方式进行代码织入,将原方法拷贝为一个新方法$Rougamo_M
,然后修改原方法进行代码织入后调用$Rougamo_M
,简化代码如下:
// 将原方法M拷贝为$Rougamo_M
public void $Rougamo_M()
{
Console.WriteLine(1);
Console.WriteLine(2);
Console.WriteLine(3);
}
// 修改原方法进行代码织入并调用拷贝后的原方法
public void M()
{
var context = new MethodContext(...);
var mo = new AbcAttribute();
mo.OnEntry(context);
try
{
$Rougamo_M();
mo.OnSuccess(context);
}
catch (Exception e)
{
context.Exception = e;
mo.OnException(context);
throw;
}
finally
{
mo.OnExit(context);
}
}
不同织入方法带来的影响
ExMoAttribute的弃用
早在1.2版本中新增了ExMoAttribute
,可能很多朋友对此都并不了解,ExMoAttribute
主要用来屏蔽使用和不使用async/await语法所带来的差异,因为使用async/await语法后,在编译时会生成对应的状态机类型,那么肉夹馍就会对应修改状态机代码进行织入,而不使用async/await语法的方法就只能对原方法进行代码织入,下面用代码简单演示其中的差异:
public async Task Delay()
{
Console.WriteLine(1);
await Task.Delay(2000);
Console.WriteLine(2);
}
// 使用async/await语法调用Delay
public async Task WithSyntax()
{
var context = new MethodContext(...);
var mo = new AbcAttribute();
mo.OnEntry(context);
try
{
await Delay();
mo.OnSuccess(context);
}
catch (Exception e)
{
context.Exception = e;
mo.OnException(context);
throw;
}
finally
{
mo.OnExit(context);
}
}
// 不使用async/await语法调用Delay
public Task WithoutSyntax()
{
var context = new MethodContext(...);
var mo = new AbcAttribute();
mo.OnEntry(context);
try
{
var task = Delay();
mo.OnSuccess(context);
return task;
}
catch (Exception e)
{
context.Exception = e;
mo.OnException(context);
throw;
}
finally
{
mo.OnExit(context);
}
}
在上面的代码示例中,没有使用async/await的WithoutSyntax
方法会在Delay
还没有执行完毕之前就执行OnSuccess
和OnExit
方法。
ExMoAttribute
针对没有使用async/await语法的方法通过在OnSuccess
方法中使用ContinueWith
达到在异步方法实际执行完毕后执行OnExit
系列方法。ExMoAttribute
虽然能够解决语法差异带来的问题,但也增加了一定的复杂性,同时因为其可能鲜为人知,所以在使用时因语法差异带来的问题可能后知后觉。
在3.0版本中由于使用代理调用的方式,被代理方法是否使用async/await语法是被屏蔽的,代理方只需要知道你的返回值是Task
即可,所以在3.0版本中MoAttribute
即可应对是否使用async/await语法的两种情况,ExMoAttribute
在3.0版本中标记为Obsolete并将在4.0版本中直接删除。下面用代码简单说明3.0对于是否使用async/await语法的统一处理方式:
// 拷贝WithSyntax原方法为$Rougamo_WithSyntax
public async Task $Rougamo_WithSyntax()
{
await Delay();
}
// 拷贝WithoutSyntax原方法为$Rougamo_WithoutSyntax
public Task $Rougamo_WithoutSyntax()
{
return Delay();
}
public async Task WithSyntax()
{
// ...代码织入
try
{
await $Rougamo_WithSyntax();
}
catch
{
// ...代码织入
}
}
public async Task WithoutSyntax()
{
// ...代码织入
try
{
await $Rougamo_WithoutSyntax();
}
catch
{
// ...代码织入
}
}
async void 弱支持
如果说前面介绍的是织入方式改变带来的优势,那么这里介绍的就是劣势了。async void方法是一种特殊的异步方法,同样会生成对应的状态机类型,但调用该方法无法进行await操作,也就无法等待该方法实际执行完毕。在3.0版本之前,由于采取的是内嵌代码织入,直接修改状态机代码完成织入,所以OnExit
系列方法可以在正确的时间点执行,而在3.0版本后由于采用了代理调用的方式,所以在执行OnExit
系列方法时无法确保方法实际已经执行完毕。
关于async void的织入方式目前还在思考中,考虑到winform和wpf中可能存在不少的async void写法,代理调用的织入方式可能就无法满足目前的使用要求了,所以我将在github中发布一个issue进行投票统计,请日常开发涉及到async void的朋友移步到github( https://github.com/inversionhourglass/Rougamo/issues/68 )中进行投票,投票将在4.0版本开发末期截止。
支持步入调试
在3.0版本之前,应用了肉夹馍完成织入的方法在开发时无法进行步入调试,这是因为之前的版本没有对调试信息做对应的修改,没有去做这一功能也是因为比较复杂懒得整。在3.0修改代码织入方式后,修改对应的调试信息相对要简单许多,因此3.0版本支持步入调试
仅ref/out支持刷新参数
在2.1版本中新增刷新参数功能,支持在OnSuccess / OnException / OnExit
中通过MethodContext.Arguments
获取最新的参数值,但在3.0版本之后,由于织入代码方式的改变,此功能仅支持ref
和out
参数。
public void M(int x, out decimal y, ref string z)
{
// ...
}
// 3.0 版本之前的织入方式
public void M(int x, out decimal y, ref string z)
{
try
{
// ...
// 由于是内嵌织入,在这里可以直接获取到所有最新的参数值,所以参数x也可以更新
context.Arguments[0] = x;
context.Arguments[1] = y;
context.Arguments[2] = z;
mo.OnSuccess(context);
}
catch (Exception e)
{
// ...
// 由于是内嵌织入,在这里可以直接获取到所有最新的参数值,所以参数x也可以更新
context.Arguments[0] = x;
context.Arguments[1] = y;
context.Arguments[2] = z;
mo.OnException(context);
throw;
}
}
// 3.0 版本的织入方式
public void M(int x, out decimal y, ref string z)
{
try
{
$Rougamo_M(x, out y, ref z);
// 由于是代理调用织入,参数x在$Rougamo_M中被重新赋值后无法在外部获取,所以仅更新参数y和z
context.Arguments[1] = y;
context.Arguments[2] = z;
mo.OnSuccess(context);
}
catch (Exception e)
{
// ...
}
}
构造方法织入方式不变
由于构造方法较为特殊,readonly字段仅可在构造方法中初始化,所以无法使用代理调用的织入方式,这也表示使用肉夹馍代码织入的构造方法无法支持步入调试。
织入方式切换
新的编织方式涉及众多代码,代码织入部分的代码近乎重写,虽然做了大量的测试,但为了保证稳定性提供了降级配置,修改项目中FodyWeavers.xml
文件中Rougamo
节点配置,通过设置proxy-calling="false"
,将织入方式改回3.0版本之前的内嵌织入方式。需要注意的是,该配置仅为过渡配置,将在4.0版本中移出并最终仅保留代理织入的方式,如果代理织入的方式存在任何问题,请及时反馈。
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Rougamo proxy-calling="false" />
</Weavers>
其他更新
以下列出3.0版本相关的所有issue,有兴趣的可以直接移步github查看issue中的回复
- #36 应用Rougamo的方法支持步入调试
- #54 解决snupkg报checksum错误的问题,需直接依赖Fody,详见issue回复
- #60 支持自定义AsyncMethodBuilder
- #63 支持泛型Attribute
- #65 修复特定Type类型无法作为MoAttribute构造方法参数
.NET静态代码织入——肉夹馍(Rougamo)发布3.0的更多相关文章
- .NET静态代码织入——肉夹馍(Rougamo) 发布1.4.0
肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应 ...
- .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)
肉夹馍是什么 肉夹馍通过静态代码织入方式实现AOP的组件..NET常用的AOP有Castle DynamicProxy.AspectCore等,以上两种AOP组件都是通过运行时生成一个代理类执行AOP ...
- 30个类手写Spring核心原理之AOP代码织入(5)
本文节选自<Spring 5核心原理> 前面我们已经完成了Spring IoC.DI.MVC三大核心模块的功能,并保证了功能可用.接下来要完成Spring的另一个核心模块-AOP,这也是最 ...
- Spring的LoadTimeWeaver(代码织入)
在Java 语言中,从织入切面的方式上来看,存在三种织入方式:编译期织入.类加载期织入和运行期织入.编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中:而类加载期织入则指通过特 ...
- Spring的LoadTimeWeaver(代码织入)(转)
https://www.cnblogs.com/wade-luffy/p/6073702.html 在Java 语言中,从织入切面的方式上来看,存在三种织入方式:编译期织入.类加载期织入和运行期织入. ...
- 【开源】.Net Aop(静态织入)框架 BSF.Aop
BSF.Aop .Net 免费开源,静态Aop织入(直接修改IL中间语言)框架,类似PostSharp(收费): 实现前后Aop切面和INotifyPropertyChanged注入方式. 开源地址: ...
- Java AOP (1) compile time weaving 【Java 切面编程 (1) 编译期织入】
According to wikipedia aspect-oriented programming (AOP) is a programming paradigm that aims to inc ...
- AOP静态代理解析2-代码织入
当我们完成了所有的AspectJ的准备工作后便可以进行织入分析了,首先还是从LoadTimeWeaverAwareProcessor开始. LoadTimeWeaverAwareProcessor实现 ...
随机推荐
- AndroidStudio开发体温上报安卓APP------问题总结
总结一下出现的问题: 1.首先是AndroidStudio的配置问题 在这里可以看sdk版本配置 这里可以看gradle的版本信息和下载目录 AndroidStudio手动配置gradle 1.首先编 ...
- 7 JavaScript循环语句
7 循环语句 在js中有三种循环语句. 首先是while循环. 它的逻辑和咱们python中的while几乎一模一样, 就是符号上有些许的区别. // 语法 while(条件){ 循环体 -> ...
- #倍增FFT#CF755G PolandBall and Many Other Balls
题目 有一排 \(n\) 个球,定义一个组可以只包含一个球或者包含两个相邻的球. 现在一个球只能分到一个组中,求从这些球中取出 \(k\) 组的方案数. \(n\leq 10^9 ,k<2^{1 ...
- 12个月大厂主机免费领AWS Azure Google-Cloud还不快到碗里来
目录 简介 AWS Azure Google Cloud Oracle 总结 简介 最近有个朋友问我哪里有免费主机可以领,说实话这个问题也困扰了我很久,之前也在网上寻找免费主机,可是免费的基本上都有一 ...
- 基于OpenHarmony的智能金属探测器
一.简介 智能金属探测器是基于 OpenAtom OpenHarmony(以下简称"OpenHarmony")操作系统,利用电磁感应原理来探测周围的金属物体.该样例采用多设备协同 ...
- Avalonia的自定义用户组件
Avalonia中的自定义用户控件 Avalonia是一个跨平台的.NET UI框架,它允许开发者使用C#和XAML来构建丰富的桌面应用程序. 自定义用户控件(UserControl)是Avaloni ...
- 从零开始学Spring Boot系列-SpringApplication
SpringApplication类提供了一种从main()方法启动Spring应用的便捷方式.在很多情况下, 你只需委托给 SpringApplication.run这个静态方法 : @Spring ...
- Chrome浏览器前端开发调试时强制更新js、css静态资源文件缓存的方法
以Chrome浏览器为例,国产浏览器未做全面测试.前端开发静态文件时,浏览器访问会缓存样式.图片.js等,怎么快速更新缓存. 以下方法特别适合只想清除某个网页的缓存,而不想清除全部浏览器缓存可以采用以 ...
- .NET周刊【4月第1期 2024-04-07】
国内文章 一个程序员的编年史 https://www.cnblogs.com/lunacy/p/18117213 作者拥有15年软件开发经验,曾在多家公司工作,项目和团队起伏充满变数.2007年,在太 ...
- HarmonyOS应用窗口管理(Stage模型)
一. 窗口开发概述 窗口模块的定义 窗口模块用于在同一块物理屏幕上,提供多个应用界面显示.交互的机制. ● 对应用开发者而言,窗口模块提供了界面显示和交互能力. ● 对终端用户而言,窗口模块提供 ...