Skip to content

应用方式

ihourglass edited this page Dec 16, 2024 · 4 revisions

应用方式

方法级Attribute应用

最简单直接的方式,直接将切面 Attribute 应用到方法上时,无需通过FeaturesPattern配置 方法匹配 规则,配置了也无效,所以这种方式是应用即生效:

public class TestAttribute : MoAttribute { }

public class Abc
{
    [Test]
    public static async ValueTask M() => await Task.Yield();
}

类和程序集级Attribute应用

切面 Attribute 除了能够应用到方法上,还可以直接应用到类和程序集上,相比直接应用到方法上,这种方式能够做到批量应用,不用到方法上一个一个写 Attribute 了,满足筛选条件的方法将全部应用该 Attribute:

// 筛选方法的方式一:通过粗粒度特征匹配,下面展示的特征为“所有共有方法”
[Pointcut(AccessFlags.Public | AccessFlags.Method)]
// 筛选方法的方式二:通过类AspectJ表达式匹配,下面展示的筛选条件是返回值 Task<int> 或 ValueTask<int> 且名称以 Get 开头的方法(不匹配属性和构造方法)
[Pointcut("method(async int Get*(..))")]
public class TestAttribute : MoAttribute
{
}

// 应用于类上
[Test]
public class Service
{
    // ...
}

// 应用于程序集上
[assembly: Test]

上面展示了筛选方法的两种方式,想要了解更多,请跳转 方法匹配

实现空接口IRougamo

上面展示的方式中,无论是将 Attribute 直接应用到方法上,还是应用到类或程序集上,都需要实际应用 AOP 的项目手动写额外的代码。如果你在编写一个基础组件,在定义一些基础接口和抽象类时,希望其他开发人员在定义实现了该接口或继承自该抽象类的类型时能够自动应用指定切面类型,比如你定义了一个IService基础接口,希望 Service 层所有实现了IService的类型都能够通过肉夹馍统计方法耗时,此时便可以通过让IService实现空接口IRougamo<>达到这一目的:

// 统计方法执行耗时的肉夹馍切面类型
public class StatisticAttribute : MoAttribute { }

// 在统一基础接口上直接应用 IRougamo 空接口
public interface IService : IRougamo<StatisticAttribute> { }

// 业务接口实现基础接口
public interface IXxService : IService { }

// Service 实现业务接口,如果没有定义业务接口,也可以直接实现 IService
public class XxService : IXxService { }

在上面的示例中,StatisticAttributeIService可以定义在你的基础组件中,IXxServiceXxService则是定义在实际的业务项目中。这种方式可以让业务项目无感知的应用AOP,是一种推荐的低侵入式的方式。

Attribute代理

如果你已经使用一些第三方组件对一些方法进行了 Attribute 标记,现在你希望对这些标记过的方法执行 AOP 操作,但又不想一个一个手动增加 Rougamo 的 Attribute标记,此时便可以通过代理的方式一步完成 AOP 织入。

比如你的项目现在有很多标记了ObsoleteAttribute的过时方法,你希望在过期方法在被调用时输出调用堆栈日志,用来排查现在哪些入口在使用这些过期方法,也可以通过该方式完成:

public class ObsoleteProxyMoAttribute : MoAttribute
{
    public override void OnEntry(MethodContext context)
    {
        Log.Warning("过期方法被调用了:" + Environment.StackTrace);
    }
}

// 在程序集级别进行代理,所有标记了 ObsoleteAttribute 的方法都将应用 ObsoleteProxyMoAttribute
[assembly: MoProxy(typeof(ObsoleteAttribute), typeof(ObsoleteProxyMoAttribute))]

public class Cls
{
    [Obsolete]
    private int GetId()
    {
        // 该方法将应用织入代码
        return 123;
    }
}

配置化

Rougamo 5.0 支持通过配置匹配方法并应用切面类型,这种方式不需要新增或修改任何 C# 代码,同时可以结合 Cli4Fody 完成非侵入式的 AOP 应用。

Rougamo 属于 Fody 插件,在引用任何 Fody 插件后,编译时会在项目目录生成一个FodyWeavers.xml配置文件,每个 Fody 插件都会在该配置文件中产生一个对应的一级节点。(没有对应的一级节点表示该插件未生效)

<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <Rougamo /> <!--编译时生成的默认配置-->
</Weavers>

Rougamo 可以通过该配置文件完成 AOP 应用:

<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <Rougamo>
    <Mos>
      <Mo assembly="Rougamo.OpenTelemetry" type="Rougamo.OpenTelemetry.OtelAttribute" pattern="method(* *Service.*(..))"/>
    </Mos>
  </Rougamo>
</Weavers>

上面的配置中,每一个Mo节点为一条应用规则,Mos节点下可以定义多个Mo节点,下面是Mo节点的属性说明:

  • type,切面类型全名称
  • assembly,切面类型所在程序集名称(不要.dll后缀)
  • pattern,AspectN 表达式,切面类型type将应用到该表达式匹配的方法上。该配置可选,未设置时将采用切面类型type自身的匹配规则

配合 Cli4Fody 可在 CI 步骤中进行配置,实现非侵入式的 AOP 应用。将上面的配置转换为 Cli4Fody 参数后:

fody-cli MySolution.sln \
          --addin Rougamo -pv 5.0.0 \
              -n Mos:Mo \
                -a assembly=Rougamo.OpenTelemetry \
                -a type=Rougamo.OpenTelemetry.OtelAttribute \
                -a pattern="method(* *Service.*(..))"