肉夹馍(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. 将CSV格式或者EXCEL格式的文件导入到HIVE数据仓库中

    学习内容:数据导入,要求将CSV格式或者EXCEL格式的文件导入到HIVE数据仓库中: ①hive建表:test1 create table test1 (InvoiceNo String, Stoc ...

  2. 论文阅读 Predicting Dynamic Embedding Trajectory in Temporal Interaction Networks

    6 Predicting Dynamic Embedding Trajectory in Temporal Interaction Networks link:https://arxiv.org/ab ...

  3. Python常用标准库(pickle序列化和JSON序列化)

    常用的标准库 序列化模块 import pickle 序列化和反序列化 把不能直接存储的数据变得可存储,这个过程叫做序列化.把文件中的数据拿出来,回复称原来的数据类型,这个过程叫做反序列化. 在文件中 ...

  4. .NET Core中JWT+Auth2.0实现SSO,附完整源码(.NET6)

    一.简介 单点登录(SingleSignOn,SSO) 指的是在多个应用系统中,只需登录一次,就可以访问其他相互信任的应用系统. JWT Json Web Token,这里不详细描述,简单说是一种认证 ...

  5. drools决策表的简单使用

    目录 一.背景 二.一个简单的决策表 1.在同一个决策表中处理多个Sheet页 2.RuleSet下方可以有哪些属性 3.RuleTable下方可以有哪些属性 4.规则属性的编写 三.需求 四.实现 ...

  6. Cpp的赋值和变量说明

    一命名方式: 1.关键字不能作为变量名 int int;是错误的电脑会提示为非法取名 上面的示例是错误示范,而错误提示告诉了为什么错了记住这错误提示了: 2.的二个知识点: 变量名是分大小写的: in ...

  7. MASA Auth - SSO与Identity设计

    AAAA AAAA即认证.授权.审计.账号(Authentication.Authorization.Audit.Account).在安全领域我们绕不开的两个问题: 授权过程可靠:让第三方程序能够访问 ...

  8. [学习笔记]使用Docker+Jenkin自动化流水线发布.Net应用

    ​使用Docker容器方案可以快速安全地将项目部署到客户的服务器上,作为公司项目,需要解决两个问题: 1. 需要搭建一个私有的Docker仓库,以便安全的存储镜像 2. 需要一套自动化发布方案,实现代 ...

  9. MathType7安装使用及please restart word to load mathtype addin properly的问题

    MathType7安装使用及please restart word to load mathtype addin properly的问题.最近在自己的电脑上安装Mathtype7,把遇到的问题和解决办 ...

  10. 如何提高访问 GitHub 的速度

    更新记录 本文迁移自Panda666原博客,原发布时间:2021年5月11日. 因为一些特殊的原因,国内访问Github的速度确实比较慢.国内访问Github经常会出现连接不上.图片加载不出来.文件无 ...