肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应用启动的初始化时间让服务更快可用,同时还能对静态方法进行AOP。

上一篇文章 中介绍了1.0.0版本肉夹馍的功能,1.0.0版本能够进行的AOP操作主要是日志记录以及APM操作,给出的示例项目也是OpenTelemetry的APM项目。在上一篇文章的评论以及github issue中都有朋友询问是否能处理异常以及修改返回值等操作,最终拖了较长一段时间于近期发布了1.1.0版本实现了这些功能。

快速开始

# 添加NuGet引用
dotnet add package Rougamo.Fody
public class TestService
{
[Fact]
public async void Test1()
{
var v1 = await M1();
Assert.Null(v1); var v2 = Sum(1, null);
Assert.Equal(-1, v2); var v3 = await M2();
Assert.Empty(v3);
} [MuteException]
public async Task<string> M1()
{
throw new NotImplementedException();
} [ArgNullCheck]
public int Sum(int? a, int? b)
{
return a.Value + b.Value;
} [ReturnNullCheck]
public async Task<string> M2()
{
await Task.Yield();
return null;
}
} public class MuteExceptionAttribute : MoAttribute
{
public override void OnException(MethodContext context)
{
if (context.RealReturnType == typeof(string))
{
context.HandledException(this, null);
}
}
} public class ArgNullCheckAttribute : MoAttribute
{
public override void OnEntry(MethodContext context)
{
foreach (var arg in context.Arguments)
{
if (arg == null)
{
context.ReplaceReturnValue(this, -1);
}
}
}
} public class ReturnNullCheckAttribute : MoAttribute
{
public override void OnSuccess(MethodContext context)
{
if (context.ReturnValue == null)
{
context.ReplaceReturnValue(this, string.Empty);
}
}
}

在上面的示例代码中MuteExceptionAttribute重写了OnException通过MethodContext.HandledException表明异常已处理并将返回值设置为null

ArgNullCheckAttribute重写了OnEntry通过MethodContext.ReplaceReturnValue设置了返回值,由于OnEntry是在执行方法前调用,这种方式会在OnEntry执行完毕之后直接将ReplaceReturnValue设置的返回值作为方法的返回值直接返回,一般参数验证、缓存逻辑会用到;

ReturnNullCheckAttribute重写了OnSuccess通过MethodContext.ReplaceReturnValue修改了实际的返回值,示例中通过这种方式避免返回null值。

注意事项

  • 如果方法是async Task那么MethodContext.RealReturnType取值为typeof(void),如果是async Task<T>那么取值为typeof(T),但如果返回值为TaskTask<T>但并没有使用async写法,那么其值就是typeof(Task)typeof(Task<T>),这样设定的好处是,你设置的返回值类型与该属性的值相同即可,不用考虑方法是否异步
  • 不论是异常处理还是设置/修改返回值,设置的返回值类型必须与方法定义的返回类型(MethodContext.RealReturnType)相同,类型不同时运行时会报错
  • OnExit中调用MethodContext.ReplaceReturnValue无法修改返回值

补充说明

上一篇文章 中由于是第一篇文章,介绍的东西较多,部分功能并没有在文章中详细说明,本篇由于篇幅较短,所以会补上一些说明,不过这里也不会介绍全部的,详细的介绍可以移步 github(https://github.com/inversionhourglass/Rougamo)

Iterator / AsyncIterator 不支持修改返回值和异常处理

IteratorAsyncIterator也就是下面的写法

public IEnumerable<int> Iterator(int count)
{
yield return 1;
yield return 2;
yield return 3;
} public async IAsyncEnumerable<int> AsyncIterator(int count)
{
yield return 3;
await Task.Yield();
yield return 2;
await Task.Yield();
yield return 1;
}

之所以不支持,是因为它们并不直接返回一个集合,而是返回一个状态机(StateMachine),使用foreach迭代时实际每次迭代执行状态机的MoveNext方法获取本次迭代的返回值,考虑到实现这种特殊机制的复杂性以及平时使用的频率,当前对此种类型不进行支持。

Iterator / AsyncIterator 不支持记录返回值

同样的,IteratorAsyncIterator默认也无法通过MethodContext.ReturnValue获取方法的返回值,但可以通过FodyWeavers.xmlRougamo节点增加属性配置enumerable-returns="true"来记录IteratorAsyncIterator的返回值到MethodContext.ReturnValue

<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Rougamo enumerable-returns="true" />
</Weavers>

这个设定是因为状态机并没有保存所有的元素到一个集合中,每个元素都是一次一次调用MoveNext执行代码返回的,如果你使用foreach遍历IteratorAsyncIterator,并且对每次遍历的元素使用玩之后并没有进行保存,那么上一个元素可能在你遍历下一个元素时被GC回收。记录它们的返回值的实现方式是额外建立一个集合保存每次迭代的元素值,这种方式对上面说的的foreach遍历的情况来说会产生额外的内存消耗,而如果迭代器的元素很多,或者每个元素本身很占内存,那么这种方式可能会额外占用大量内存空间,所以开启这个开关前需要考虑一番。

最后

如果在使用肉夹馍的过程中遇到了什么问题,或者希望增加一些什么样的功能,欢迎到github(https://github.com/inversionhourglass/Rougamo)里提issue,不过对于新功能,可能会有一个较长的周期才能完成并发布正式版。

随着SourceGenerator的应用越来越广泛,Mono.Cecil的应用场景被进一步压缩,一开始提到的动态代理现在也能通过SourceGenerator在编译时生成代理类,这是一件好事,相比晦涩易错的IL,SourceGenerator提供的语法树更加方便易懂且不易出错,但这并不代表Mono.Cecil应该退场了(至少现在不是),Mono.Cecil虽然门槛高,但他的功能也同样强大,直接修改IL是SourceGenerator和`Emit所无法做到的(至少现在是这样),如果在以后的编程之路中遇到了SourceGenerator和`Emit无法解决的问题,希望你能想起还有Mono.CecilFody这条路,如果有时间可以尝试一下,也希望肉夹馍这个项目能给你带来一些参考价值。

.NET静态代码织入——肉夹馍(Rougamo) 发布1.1.0的更多相关文章

  1. .NET静态代码织入——肉夹馍(Rougamo) 发布1.2.0

    肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应 ...

  2. .NET静态代码织入——肉夹馍(Rougamo)

    肉夹馍是什么 肉夹馍通过静态代码织入方式实现AOP的组件..NET常用的AOP有Castle DynamicProxy.AspectCore等,以上两种AOP组件都是通过运行时生成一个代理类执行AOP ...

  3. 30个类手写Spring核心原理之AOP代码织入(5)

    本文节选自<Spring 5核心原理> 前面我们已经完成了Spring IoC.DI.MVC三大核心模块的功能,并保证了功能可用.接下来要完成Spring的另一个核心模块-AOP,这也是最 ...

  4. Spring的LoadTimeWeaver(代码织入)

    在Java 语言中,从织入切面的方式上来看,存在三种织入方式:编译期织入.类加载期织入和运行期织入.编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中:而类加载期织入则指通过特 ...

  5. Spring的LoadTimeWeaver(代码织入)(转)

    https://www.cnblogs.com/wade-luffy/p/6073702.html 在Java 语言中,从织入切面的方式上来看,存在三种织入方式:编译期织入.类加载期织入和运行期织入. ...

  6. 【开源】.Net Aop(静态织入)框架 BSF.Aop

    BSF.Aop .Net 免费开源,静态Aop织入(直接修改IL中间语言)框架,类似PostSharp(收费): 实现前后Aop切面和INotifyPropertyChanged注入方式. 开源地址: ...

  7. Java AOP (1) compile time weaving 【Java 切面编程 (1) 编译期织入】

    According to wikipedia  aspect-oriented programming (AOP) is a programming paradigm that aims to inc ...

  8. AOP静态代理解析2-代码织入

    当我们完成了所有的AspectJ的准备工作后便可以进行织入分析了,首先还是从LoadTimeWeaverAwareProcessor开始. LoadTimeWeaverAwareProcessor实现 ...

  9. 框架源码系列三:手写Spring AOP(AOP分析、AOP概念学习、切面实现、织入实现)

    一.AOP分析 问题1:AOP是什么? Aspect Oriented Programming 面向切面编程,在不改变类的代码的情况下,对类方法进行功能增强. 问题2:我们需要做什么? 在我们的框架中 ...

随机推荐

  1. 详细剖析pyecharts大屏的Page函数配置文件:chart_config.json

    目录 一.问题背景 二.揭开json文件神秘面纱 三.巧用json文件 四.关于Table图表 五.同步讲解视频 5.1 讲解json的视频 5.2 讲解全流程大屏的视频 5.3 讲解全流程大屏的文章 ...

  2. 渗透:dSploit

    dSploit--开源的专业的Android平台安全管理工具包 只能在横屏模式下工作,即使你旋转你的设备也将继续保持横屏,如果你有一个应用程序,如旋转控制器,迫使每一个应用程序旋转,将导致dSploi ...

  3. 面试官:BIO、NIO、AIO是什么,他们有什么区别?

    哈喽!大家好,我是小奇,一位热爱分享的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 书接上回,感觉上次的公司氛围不 ...

  4. mysql查询关键字补充与多表查询

    目录 查询关键字补充 having过滤 distinct去重 order by排序 limit分页 regexp正则 多表查询 子查询 连表查询 查询关键字补充 having过滤 关键字having和 ...

  5. hexo + typora 图片插入解决办法

    Typora 是一款知名的 Markdown 编辑器,简单好用,体验良好.使用 hexo 搭建好博客后,主要是用 Markdown 来编写博客,typora 便是我的首选编辑器.但直接使用 typor ...

  6. 对于vjudge在有些网络下无法打开的问题

    因为有些网络会屏蔽vjudge,所以打开 镜像网址 不行再试试这个:最新镜像网址

  7. Dubbo3 源码系列 Dubbo“纠葛”(入门篇)

    日期 更新说明 2022年5月28日 spring xml部分解读 2022年6月3日 spring annotation部分解读 人生不相见, 动如参与商. 今夕复何夕, 共此灯烛光. 少壮能几时, ...

  8. 渗透测试之sql注入验证安全与攻击性能

    由于渗透测试牵涉到安全性以及攻击性,为了便于交流分享,本人这里不进行具体网址的透露了. 我们可以在网上查找一些公司官方网站如(http://www.XXXXXX.com/xxxx?id=1) 1.拿到 ...

  9. 《The Tail At Scale》论文详解

    简介 用户体验与软件的流畅程度是呈正相关的,所以对于软件服务提供方来说,保持服务耗时在用户能接受的范围内就是一件必要的事情.但是在大型分布式系统上保持一个稳定的耗时又是一个很大的挑战,这篇文章解析的是 ...

  10. RPA应用场景-账套建立

    所涉人工数量5操作频率 不定时 场景流程 1.客户按照项目开设专项财务管理,每个项目需要在初期建立自己的账套: 2.运营专员通过邮件发送账套建立申请: 3.根据申请进入金蝶运维后台,依据规则完成账套建 ...