肉夹馍(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)
,但如果返回值为Task
或Task<T>
但并没有使用async
写法,那么其值就是typeof(Task)
或typeof(Task<T>)
,这样设定的好处是,你设置的返回值类型与该属性的值相同即可,不用考虑方法是否异步 - 不论是异常处理还是设置/修改返回值,设置的返回值类型必须与方法定义的返回类型(
MethodContext.RealReturnType
)相同,类型不同时运行时会报错 OnExit
中调用MethodContext.ReplaceReturnValue
无法修改返回值
补充说明
在 上一篇文章 中由于是第一篇文章,介绍的东西较多,部分功能并没有在文章中详细说明,本篇由于篇幅较短,所以会补上一些说明,不过这里也不会介绍全部的,详细的介绍可以移步 github(https://github.com/inversionhourglass/Rougamo)
Iterator / AsyncIterator 不支持修改返回值和异常处理
Iterator
和AsyncIterator
也就是下面的写法
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 不支持记录返回值
同样的,Iterator
和AsyncIterator
默认也无法通过MethodContext.ReturnValue
获取方法的返回值,但可以通过FodyWeavers.xml
的Rougamo
节点增加属性配置enumerable-returns="true"
来记录Iterator
和AsyncIterator
的返回值到MethodContext.ReturnValue
。
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Rougamo enumerable-returns="true" />
</Weavers>
这个设定是因为状态机并没有保存所有的元素到一个集合中,每个元素都是一次一次调用MoveNext
执行代码返回的,如果你使用foreach
遍历Iterator
或AsyncIterator
,并且对每次遍历的元素使用玩之后并没有进行保存,那么上一个元素可能在你遍历下一个元素时被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.Cecil
和Fody
这条路,如果有时间可以尝试一下,也希望肉夹馍这个项目能给你带来一些参考价值。
.NET静态代码织入——肉夹馍(Rougamo) 发布1.1.0的更多相关文章
- .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实现 ...
- 框架源码系列三:手写Spring AOP(AOP分析、AOP概念学习、切面实现、织入实现)
一.AOP分析 问题1:AOP是什么? Aspect Oriented Programming 面向切面编程,在不改变类的代码的情况下,对类方法进行功能增强. 问题2:我们需要做什么? 在我们的框架中 ...
- Spring AOP 之编译期织入、装载期织入、运行时织入(转)
https://blog.csdn.net/wenbingoon/article/details/22888619 一 前言 AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP ...
- AOP 动态织入的.NET实现
AOP(面向切面编程:Aspect Oriented Programming)为诸如日志记录.性能统计.安全控制.事务处理.异常处理等与具体业务逻辑无关,却需要在全局范围进行执行的功能提供了一种良好重 ...
随机推荐
- [No000062]读书八字诀:怎样将书读得通透?
从吃透到通透 有种说法,吃透一本书,才算好好读过.然而比吃透境界更高,是通透.吃透仅限于书中内容,通透则是将书中内容与正反上下.古今中外背景知识相互关联. 当你做到读书通透,收获将远远大于手头那一本书 ...
- 彻底删除mysql方法
首先,先在服务(开始——>控制面板——>管理工具——>服务)里停掉MySQL的服务.打开控制面板-添加删除程序,找到MySQL,卸载.或者用360安全卫士来卸载也行.也可以用mysq ...
- ViewPager -- Fragment 切换卡顿 性能优化
当ViewPager切换到当前的Fragment时,Fragment会加载布局并显示内容,如果用户这时快速切换ViewPager,即 Fragment需要加载UI内容,而又频繁地切换Fragment, ...
- CSS动画控制器
<html> <head> <title>animation</title> <style> div{ width: 100px; heig ...
- nginx主配置文件 在那找怎么打开
- springMVC源码分析[email protected]使用及运行原理
这一篇博客我们简单的介绍一下ModelAttribute的使用和运行原理. 1.首先@ModelAttribute是使用在方法或者上的,当使用在方法上时其作用于本身所在的Controller,在访问C ...
- centos6.5 配置静态IP
1.修改网卡配置 编辑:vi /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth0 BOOTPROTO=static HWADDR=08:00:2 ...
- Hello jna
记录下这几天用jna3.5.0调c++写的dll的经历 os:win7 用jna调dll首先需要一个dll文件并有可调的方法,然后根据方法的名称,参数,返回值编写一个interface c++需要包含 ...
- 【干货】利用MVC5+EF6搭建博客系统(二)测试添加数据、集成Autofac依赖注入
PS:如果图片模糊,鼠标右击复制图片网址,然后在浏览器中打开即可. 一.测试仓储层.业务层是否能实现对数据库表的操作 1.在52MVCBlog.IRepository程序集下创建IsysUserInf ...
- 使用es6的then()方法封装jquery的ajax请求
使用场景: jsp页面中使用jquery的ajax请求比较频繁,以前vue框架的项目用过axios,所以就想着用then()封装一个公共请求的方法,这样每次请求就不用那么麻烦的写一大堆请求参数了. 示 ...