中文 | English
Rougamo是一个静态代码编织的AOP组件,同为AOP组件较为常用的有Castle、Autofac、AspectCore等,与这些组件不同的是,这些组件基本都是通过动态代理+IoC的方式实现AOP,是运行时完成的,而Rougamo是编译时直接修改目标方法织入IL代码实现AOP的。肉夹馍支持包括同步/异步方法、静态/实例方法、构造方法、属性内的所有种类的方法。
也没什么由来,方法级AOP在我看来就是两面包夹芝士,在方法执行前后添加切面,将整个方法夹在中间。肉夹馍大家都吃过的,虽然叫肉夹馍,但其实是肉夹于馍,外层为馍中间为肉,Rougamo同理,外层切面我称为Mo
,中间实际方法称之为Rou
,希望Rougamo的馍能够为你们的肉增香添色。
肉夹馍作为一款AOP组件,其基本功能就是在方法几个主要生命周期节点执行额外操作,肉夹馍支持四个生命周期节点(或称为连接点 Join Point):
try..finally..
)。用一个简单示例进行说明生命周期节点在代码中的表现形式:
// 定义一个类型继承MoAttribute
public class TestAttribute : MoAttribute
{
public override void OnEntry(MethodContext context)
{
// OnEntry对应方法执行前
}
public override void OnException(MethodContext context)
{
// OnException对应方法抛出异常后
}
public override void OnSuccess(MethodContext context)
{
// OnSuccess对应方法执行成功后
}
public override void OnExit(MethodContext context)
{
// OnExit对应方法退出时
}
}
在方法的各个生命周期节点,除了能够执行类似记录日志、统计方法耗时、APM埋点等不影响方法执行的操作,还可以进行以下这些控制方法执行的操作:
最简单直接的方式便是将定义的Attribute
直接应用到方法上,这个方法可以是同步的也可以是异步的,可以是实例方法也可以是静态方法,可以是属性也可以是属性getter/setter,可以是示例构造方法也可以是静态构造方法:
class Abc
{
[Test]
static Abc() { }
[Test]
public Abc() { }
[Test]
public int X { get; set; }
public static Y
{
[Test]
get;
[Test]
set;
}
[Test]
public void M() { }
[Test]
public static async ValueTask MAsync() => await Task.Yeild();
}
直接应用到方法上的方式简单明了,但对于一些非常常用的AOP类型,每个方法都需要添加Attribute就显得非常麻烦,所以肉夹馍还提供了多种批量应用的方式:
我们在进行批量应用的时候,比如将TestAttribute
直接应用到一个类上,我们一般并不是希望这个类的所有方法都应用TestAttribute
,而是希望选定那些满足特定特征的部分方法。肉夹馍在方法筛选上提供了两种方案:
现在异步编程在日常开发中已经是非常常见的了,在最上面的示例中,方法生命周期节点对应的方法全是同步的,如果希望进行一些异步操作,就不得不手动阻塞异步操作等待返回结果了,这是存在一定的资源浪费和性能损耗的。肉夹馍除了同步切面,还提供了异步切面,更好的支持异步操作:
// 定义一个类型继承AsyncMoAttribute
public class TestAttribute : AsyncMoAttribute
{
public override async ValueTask OnEntryAsync(MethodContext context) { }
public override async ValueTask OnExceptionAsync(MethodContext context) { }
public override async ValueTask OnSuccessAsync(MethodContext context) { }
public override async ValueTask OnExitAsync(MethodContext context) { }
}
不过需要注意的是,如果并不需要做异步操作,那么还是推荐使用同步切面,更多关于异步切面的说明,请跳转 异步切面。
肉夹馍是方法级AOP组件,在对方法应用肉夹馍后,每次调用方法都会实例化肉夹馍的相关对象,增加GC的负担。虽然这些负担是轻微的,很多时候是可以忽略不计的,但肉夹馍关注其对性能的影响,所以从不同的方向提供了各种优化方式:
MethodContext
中保存了当前方法的一些上下文信息,这些信息需要使用额外的对象进行保存,同时伴随着一些装箱拆箱操作,比如方法参数、返回值等,如果确定不需要这些信息,可以通过瘦身MethodContext
达到一定的优化效果;ValueTask
来优化同步执行,但终究还是存在额外的开销,在编写异步切面时,如果确定不需要异步操作,可以通过强制调用同步切面来避免异步切面的额外开销。