SourceGenerator 使用姿势(1):生成代理类,实现简单的AOP

2023-02-12,,,

SourceGenerator 已经出来很久了,也一直在关注。之前观摩大佬 xljiulang 的 WebApiClient 使用 SourceGenerator 生成接口代理类,深受启发,准备拿过来用看看(发出白嫖的声音),写个编译期静态代理AOP。本篇重点是怎么获取元数据,得到想要的数据,生成想要的代码(往下拖到第 4 点)。

几个月前写了个demo,现在趁着有空重新整理完善了下。.net 6 新增了个 IIncrementalGenerator 进行增量编译,这个还没研究,后面再说。

我的思路是继承,生成一个类去继承需要拦截的实际类,然后重写相关的方法,此时插入额外的方法,比如 Before,After 等。这就要求相关方法必须是 可重写 的, virtualoverride。好了,开干。

1、定义Aop属性,打个标签,SourceGenerator 根据这个标签查找相关的 class 或 interface

 1     /// <summary>
2 /// Aop 拦截器
3 /// </summary>
4 public interface IAopInterceptor
5 {
6 /// <summary>
7 /// 执行前操作,同步方法调用
8 /// </summary>
9 /// <param name="context"></param>
10 /// <returns></returns>
11 AopContext Before(AopContext context);
12 /// <summary>
13 /// 执行前操作,异步方法调用
14 /// </summary>
15 /// <param name="context"></param>
16 /// <returns></returns>
17 ValueTask<AopContext> BeforeAsync(AopContext context);
18 /// <summary>
19 /// 执行后操作,同步方法调用
20 /// </summary>
21 /// <param name="context"></param>
22 /// <returns></returns>
23 AopContext After(AopContext context);
24 /// <summary>
25 /// 执行后操作,异步方法调用
26 /// </summary>
27 /// <param name="context"></param>
28 /// <returns></returns>
29 ValueTask<AopContext> AfterAsync(AopContext context);
30 /// <summary>
31 /// 执行方法,同步方法调用
32 /// </summary>
33 /// <param name="context"></param>
34 /// <returns></returns>
35 AopContext Next(AopContext context);
36 /// <summary>
37 /// 执行方法,异步方法调用
38 /// </summary>
39 /// <param name="context"></param>
40 /// <returns></returns>
41 ValueTask<AopContext> NextAsync(AopContext context);
42 }

可以不要 IAopInterceptor 这个接口,这里加了只是为了约束。

 1     public class AopInterceptor : Attribute, IAopInterceptor
2 {
3 /// <summary>
4 /// 是否执行 Before
5 /// </summary>
6 public bool HasBefore { get; set; }
7 /// <summary>
8 /// 是否执行 After
9 /// </summary>
10 public bool HasAfter { get; set; }
11 /// <summary>
12 /// 是否执行 Aop 的 Next
13 /// </summary>
14 public bool HasAopNext { get; set; }
15 /// <summary>
16 /// 是否执行实际的方法
17 /// </summary>
18 public bool HasActualNext { get; set; }
19
20 /// <summary>
21 /// 默认执行所以方法
22 /// </summary>
23 public AopInterceptor()
24 {
25 HasBefore = true;
26 HasAopNext = true;
27 HasActualNext = true;
28 HasAfter = true;
29 }
30
31 public virtual AopContext Before(AopContext context) => context;
32
33 public virtual async ValueTask<AopContext> BeforeAsync(AopContext context)
34 {
35 await ValueTask.CompletedTask;
36 return context;
37 }
38
39 public virtual AopContext After(AopContext context)
40 {
41 return context.Exception != null ? throw context.Exception : context;
42 }
43
44 public virtual async ValueTask<AopContext> AfterAsync(AopContext context)
45 {
46 if (context.Exception != null)
47 throw context.Exception;
48
49 await ValueTask.CompletedTask;
50 return context;
51 }
52
53 public virtual AopContext Next(AopContext context)
54 {
55 try
56 {
57 context.Invoke();
58 }
59 catch (Exception e)
60 {
61 context.Exception = e;
62 }
63 return context;
64 }
65
66 public virtual async ValueTask<AopContext> NextAsync(AopContext context)
67 {
68 try
69 {
70 context = await context.InvokeAsync();
71 }
72 catch (Exception e)
73 {
74 context.Exception = e;
75 }
76
77 return context;
78 }
79 }

2、定义上下文,主要包含 是否是异步,是否有返回值,还有实际方法的委托。决定了调用实际方法的时候怎么调用

 1     /// <summary>
2 /// Aop 上下文
3 /// </summary>
4 public struct AopContext
5 {
6 /// <summary>
7 /// 是否是异步
8 /// </summary>
9 public bool IsTask { get; private set; }
10 /// <summary>
11 /// 是否有返回值
12 /// </summary>
13 public bool HasReturnValue { get; private set; }
14 /// <summary>
15 /// 方法输入参数
16 /// </summary>
17 public Dictionary<string, dynamic> MethodInputParam { get; private set; }
18
19 /// <summary>
20 /// 实际方法执行结果,可能是 Task
21 /// </summary>
22 public Func<dynamic> ActualMethod { get; set; }
23 /// <summary>
24 /// 返回值,具体的值
25 /// </summary>
26 public dynamic ReturnValue { get; set; }
27 /// <summary>
28 /// 异常信息
29 /// </summary>
30 public Exception Exception { get; set; }
31 /// <summary>
32 /// IServiceProvider
33 /// </summary>
34 public IServiceProvider ServiceProvider { get; private set; }
35
36 /// <summary>
37 /// 初始化
38 /// </summary>
39 /// <param name="serviceProvider"></param>
40 /// <param name="methodInputParam"></param>
41 /// <param name="isTask"></param>
42 /// <param name="hasReturnValue"></param>
43 /// <param name="actualMethod"></param>
44 public AopContext(IServiceProvider serviceProvider, Dictionary<string, dynamic> methodInputParam, bool isTask, bool hasReturnValue, Func<dynamic> actualMethod) : this()
45 {
46 ServiceProvider = serviceProvider;
47 MethodInputParam = methodInputParam;
48 IsTask = isTask;
49 HasReturnValue = hasReturnValue;
50 ActualMethod = actualMethod;
51 }
52
53 /// <summary>
54 /// 执行实际方法 异步
55 /// </summary>
56 /// <returns></returns>
57 public async ValueTask<AopContext> InvokeAsync()
58 {
59 if (ActualMethod == null)
60 return this;
61
62 if (HasReturnValue)
63 {
64 ReturnValue = await ActualMethod();
65 return this;
66 }
67
68 await ActualMethod();
69 return this;
70 }
71
72 /// <summary>
73 /// 执行实际方法 同步
74 /// </summary>
75 /// <returns></returns>
76 public void Invoke()
77 {
78 if (ActualMethod == null)
79 return;
80
81 //特殊处理 同步且没有返回值,用 Task.Run 包装
82 if (!IsTask && !HasReturnValue)
83 ActualMethod.Invoke().GetAwaiter().GetResult();
84 else
85 ReturnValue = ActualMethod.Invoke();
86 }
87 }

3、硬编码实现类

3.1、定义拦截器

 1     /// <summary>
2 /// 常规服务,执行所有方法
3 /// </summary>
4 public class SampleAttribute : AopInterceptor
5 {
6 /// <summary>执行前操作,同步方法调用</summary>
7 /// <param name="context"></param>
8 /// <returns></returns>
9 public override AopContext Before(AopContext context)
10 {
11 Console.WriteLine("Before...");
12 return base.Before(context);
13 }
14
15 /// <summary>执行前操作,异步方法调用</summary>
16 /// <param name="context"></param>
17 /// <returns></returns>
18 public override ValueTask<AopContext> BeforeAsync(AopContext context)
19 {
20 Console.WriteLine("BeforeAsync...");
21 return base.BeforeAsync(context);
22 }
23
24 public override AopContext After(AopContext context)
25 {
26 Console.WriteLine("After...");
27 return context;
28 }
29
30 /// <summary>执行后操作,异步方法调用</summary>
31 /// <param name="context"></param>
32 /// <returns></returns>
33 public override ValueTask<AopContext> AfterAsync(AopContext context)
34 {
35 Console.WriteLine("AfterAsync...");
36 return base.AfterAsync(context);
37 }
38
39 /// <summary>执行方法,同步方法调用</summary>
40 /// <param name="context"></param>
41 /// <returns></returns>
42 public override AopContext Next(AopContext context)
43 {
44 Console.WriteLine("Next...");
45 return base.Next(context);
46 }
47
48 /// <summary>执行方法,异步方法调用</summary>
49 /// <param name="context"></param>
50 /// <returns></returns>
51 public override ValueTask<AopContext> NextAsync(AopContext context)
52 {
53 Console.WriteLine("NextAsync...");
54 return base.NextAsync(context);
55 }
56 }

定义接口

1     public interface ITestService
2 {
3 [Sample]
4 DateTime SampleSync();
5
6 [Sample]
7 ValueTask<DateTime> SampleAsync();
8 }

3.2、定义实现类

 1     public class TestService : ITestService
2 {
3
4 public virtual DateTime SampleSync()
5 {
6 return DateTime.Now;
7 }
8
9 public virtual async ValueTask<DateTime> SampleAsync()
10 {
11 await ValueTask.CompletedTask;
12 return DateTime.Now;
13 }
14 }

3.3、定义继承类,重写相关方法

 1     public sealed class TestService_Aop : TestService
2 {
3 private readonly IServiceProvider _serviceProvider0;
4 public TestService_Aop(IServiceProvider serviceProvider0)
5 {
6 _serviceProvider0 = serviceProvider0;
7 }
8
9 public override DateTime SampleSync()
10 {
11 var aopContext = new AopContext(_serviceProvider0,
12 new Dictionary<string, dynamic>() { },
13 false,
14 true,
15 null);
16
17 var aopInterceptor0 = _serviceProvider0.GetRequiredService<SampleAttribute>();
18 if (aopInterceptor0.HasBefore) aopContext = aopInterceptor0.Before(aopContext);
19 if (aopInterceptor0.HasAopNext)
20 {
21 if (aopInterceptor0.HasActualNext)
22 {
23 aopContext.ActualMethod = () => base.SampleSync();
24 }
25 aopContext = aopInterceptor0.Next(aopContext);
26 }
27 else
28 {
29 if (aopInterceptor0.HasActualNext)
30 {
31 aopContext.ReturnValue = base.SampleSync();
32 }
33 }
34 if (aopInterceptor0.HasAfter) aopContext = aopInterceptor0.After(aopContext);
35
36 return aopContext.ReturnValue;
37 }
38
39 public override async ValueTask<DateTime> SampleAsync()
40 {
41 var aopContext = new AopContext(_serviceProvider0,
42 new Dictionary<string, dynamic>() { },
43 true,
44 true,
45 null);
46
47 var aopInterceptor0 = _serviceProvider0.GetRequiredService<SampleAttribute>();
48 if (aopInterceptor0.HasBefore) aopContext = await aopInterceptor0.BeforeAsync(aopContext);
49 if (aopInterceptor0.HasAopNext)
50 {
51 if (aopInterceptor0.HasActualNext)
52 {
53 aopContext.ActualMethod = () => base.SampleAsync();
54 }
55 aopContext = await aopInterceptor0.NextAsync(aopContext);
56 }
57 else
58 {
59 if (aopInterceptor0.HasActualNext)
60 {
61 aopContext.ReturnValue = await base.SampleAsync();
62 }
63 }
64 if (aopInterceptor0.HasAfter) aopContext = await aopInterceptor0.AfterAsync(aopContext);
65
66 return aopContext.ReturnValue;
67 }
68 }

4、开整

4.1、新建项目 Mic.Aop.Generator,TargetFramework 选 netstandard2.0,引入两个分析器包

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

4.2、新建类 AopGenerator,继承 ISourceGenerator 接口,实现 Execute 方法,Execute 的内容是最终的成品。

 1     /// <summary>
2 /// 代码生成器
3 /// </summary>
4 [Generator]
5 public class AopGenerator : ISourceGenerator
6 {
7 /// <summary>
8 /// 初始化
9 /// </summary>
10 /// <param name="context"></param>
11 public void Initialize(GeneratorInitializationContext context)
12 {
13 //Debugger.Launch();
14
15 context.RegisterForSyntaxNotifications(() => new AopSyntaxReceiver());
16 }
17
18 /// <summary>
19 /// 执行
20 /// </summary>
21 /// <param name="context"></param>
22 public void Execute(GeneratorExecutionContext context)
23 {
24 if (context.SyntaxReceiver is AopSyntaxReceiver receiver)
25 {
26 var aopMateData = receiver
27 .FindAopInterceptor() // 查找所有的拦截器
28 .GetAopMetaData(context.Compilation); //根据拦截器找到所有的类或方法,获取元数据,包含所有接口、实现类、所有属性、所有方法
29
30 var builders = aopMateData
31 .GetAopCodeBuilderMetaData() //获取用于构建代码的元数据,过滤出需要的数据
32 .Select(i => new AopCodeBuilder(i))
33 .Distinct()
34 .ToList();
35 //开始生成代码
36 foreach (var builder in builders)
37 {
38 context.AddSource(builder.SourceCodeName, builder.ToSourceText());
39 }
40 }
41 }
42 }

4.3、AopSyntaxReceiver 语法树处理类,这一步获取到所有的数据:接口、类、属性、方法、参数等等等

    /// <summary>
/// 语法接收器
/// </summary>
sealed class AopSyntaxReceiver : ISyntaxReceiver
{
private const string GeneratorTagName = "AopInterceptor"; //所有拦截器需要继承的基类
private const string IgnoreAttribute = "IgnoreAopAttribute"; //忽略aop
/// <summary>
/// 类列表
/// </summary>
private readonly List<ClassDeclarationSyntax> _classSyntaxList = new List<ClassDeclarationSyntax>();
/// <summary>
/// 接口列表
/// </summary>
private readonly List<InterfaceDeclarationSyntax> _interfaceSyntaxList = new List<InterfaceDeclarationSyntax>();
/// <summary>
/// 所有的AopInterceptor
/// </summary>
public List<string> AopAttributeList = new List<string>();
/// <summary>
/// 所有的AopInterceptor
/// </summary>
public List<ClassMetaData> AopAttributeClassMetaDataList = new List<ClassMetaData>(); /// <summary>
/// 访问语法树
/// </summary>
/// <param name="syntaxNode"></param>
void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is InterfaceDeclarationSyntax interfaceSyntax)
{
this._interfaceSyntaxList.Add(interfaceSyntax);
} if (syntaxNode is ClassDeclarationSyntax classSyntax)
{
this._classSyntaxList.Add(classSyntax);
}
} //其他代码........
}

4.4、找到所有的拦截器

 1         /// <summary>
2 /// 找出所有 AopInterceptor
3 /// </summary>
4 /// <returns></returns>
5 public AopSyntaxReceiver FindAopInterceptor()
6 {
7 foreach (var classSyntax in this._classSyntaxList)
8 {
9 var root = classSyntax.SyntaxTree.GetRoot();
10 var classesWithAttribute = root
11 .DescendantNodes()
12 .OfType<ClassDeclarationSyntax>()
13 .ToList();
14
15 if (!classesWithAttribute.Any())
16 continue;
17
18 foreach (var classDeclarationSyntax in classesWithAttribute)
19 {
20 if (classDeclarationSyntax.BaseList == null)
21 continue;
22
23 foreach (BaseTypeSyntax baseTypeSyntax in classDeclarationSyntax.BaseList.Types)
24 {
25 if (baseTypeSyntax.ToString().Trim() == GeneratorTagName)
26 {
27 AopAttributeList.Add(classDeclarationSyntax.Identifier.Text);
28
29 var meta = GetClassMetaData(classSyntax);
30 if (meta != null && AopAttributeClassMetaDataList.All(d => d.Name != meta.Name))
31 AopAttributeClassMetaDataList.Add(meta);
32 }
33 }
34 }
35 }
36
37 AopAttributeList = AopAttributeList.Distinct().ToList();
38
39 return this;
40 }

4.5、找到所有接口和打了标记的class

 1         /// <summary>
2 /// 获取所有打了标记的接口和类
3 /// </summary>
4 /// <param name="compilation"></param>
5 /// <returns></returns>
6 public AopMetaData GetAopMetaData(Compilation compilation)
7 {
8 var result = new AopMetaData(AopAttributeList, IgnoreAttribute, new List<InterfaceMetaData>(), new List<ClassMetaData>());
9
10 if (!AopAttributeList.Any())
11 return result;
12
13 //处理接口
14 foreach (var classSyntax in this._interfaceSyntaxList)
15 {
16 var root = classSyntax.SyntaxTree.GetRoot();
17 var interfaceWithAttribute = root
18 .DescendantNodes()
19 .OfType<InterfaceDeclarationSyntax>()
20 .ToList();
21
22 if (!interfaceWithAttribute.Any())
23 continue;
24
25 //处理接口
26 foreach (var interfaceDeclaration in interfaceWithAttribute)
27 {
28 var namespaceName = interfaceDeclaration.FindParent<NamespaceDeclarationSyntax>().Name.ToString();
29 var className = interfaceDeclaration.Identifier.Text;
30 var properties = interfaceDeclaration.DescendantNodes().OfType<PropertyDeclarationSyntax>().ToList();
31 var methodSyntaxs = interfaceDeclaration.DescendantNodes().OfType<MethodDeclarationSyntax>().ToList();
32
33 //属性集合
34 var props = properties.Select(d => new PropertyMetaData(d.Identifier.Text, d.GetAttributeMetaData())).ToList();
35 //方法集合
36 var methods = methodSyntaxs.Select(GetMethodMetaData).ToList();
37
38 var interfaceMetaData = new InterfaceMetaData(namespaceName, className, interfaceDeclaration.GetAttributeMetaData(), props, methods);
39 if (interfaceMetaData.MethodMetaData.Any() && !result.InterfaceMetaDataList.Exists(d => d.Equals(interfaceMetaData)))
40 result.InterfaceMetaDataList.Add(interfaceMetaData);
41 }
42 }
43
44 //处理类
45 foreach (var classSyntax in this._classSyntaxList)
46 {
47 var root = classSyntax.SyntaxTree.GetRoot();
48 var classesWithAttribute = root
49 .DescendantNodes()
50 .OfType<ClassDeclarationSyntax>()
51 .ToList();
52
53 if (!classesWithAttribute.Any())
54 continue;
55
56 foreach (var classDeclaration in classesWithAttribute)
57 {
58 var classMetaData = GetClassMetaData(classDeclaration);
59 if (classMetaData == null)
60 continue;
61
62 if (AopAttributeList.Contains(classMetaData.Name))
63 continue;
64
65 if (classMetaData.MethodMetaData.Any() && !result.ClassMetaDataList.Exists(d => d.Equals(classMetaData)))
66 result.ClassMetaDataList.Add(classMetaData);
67 }
68 }
69
70 result.AopAttributeClassMetaDataList = AopAttributeClassMetaDataList;
71
72 return result;
73 }

4.6、获取 class 的信息:属性、方法集合、继承的接口、using引用、构造函数

 1         private ClassMetaData? GetClassMetaData(ClassDeclarationSyntax classDeclaration)
2 {
3 var namespaceName = classDeclaration.FindParent<NamespaceDeclarationSyntax>().Name.ToString();
4 var className = classDeclaration.Identifier.Text;
5 var properties = classDeclaration.DescendantNodes().OfType<PropertyDeclarationSyntax>().ToList();
6 var methodSyntaxs = classDeclaration.DescendantNodes().OfType<MethodDeclarationSyntax>().ToList();
7
8 //属性集合
9 var props = properties.Select(d => new PropertyMetaData(d.Identifier.Text, d.GetAttributeMetaData())).ToList();
10 //方法集合
11 var methods = methodSyntaxs.Select(GetMethodMetaData).ToList();
12 //实现的接口集合
13 var interfaces = classDeclaration.BaseList?.ToString().Split(':').Last().Trim().Split(',').Where(d => d.Split('.').Last().StartsWith("I")).ToList() ?? new List<string>();
14 //using 引用
15 var usingDirectiveSyntax = classDeclaration.Parent?.Parent == null ? new SyntaxList<UsingDirectiveSyntax>() : ((CompilationUnitSyntax)classDeclaration.Parent.Parent).Usings;
16 var usings = usingDirectiveSyntax.Select(d => d.ToString()).ToList();
17
18 //构造函数
19 var constructorDictionary = new List<KeyValueModel>();
20 foreach (var memberDeclarationSyntax in classDeclaration.Members)
21 {
22 if (memberDeclarationSyntax.Kind().ToString() == "ConstructorDeclaration")
23 {
24 //constructorDictionary = memberDeclarationSyntax.DescendantNodes().OfType<ParameterSyntax>().ToDictionary(d => d.GetFirstToken().Text, d => d.GetLastToken().Text);
25 constructorDictionary = memberDeclarationSyntax.DescendantNodes().OfType<ParameterSyntax>().Select(d => new KeyValueModel(d.Type?.ToString(), d.Identifier.Text)).ToList();
26 break;
27 }
28 }
29
30 return new ClassMetaData(namespaceName, className, classDeclaration.GetAttributeMetaData(), props, methods, interfaces, constructorDictionary, usings);
31 }

4.7、获取 method 的信息:方法名称、是否异步、是否有返回值、是否可重写、参数信息、Aop 标记集合(可能有多个)

 1         private MethodMetaData GetMethodMetaData(MethodDeclarationSyntax methodDeclarationSyntax)
2 {
3 var param = new List<KeyValueModel>();
4 var properties = methodDeclarationSyntax.DescendantNodes().OfType<ParameterListSyntax>().FirstOrDefault()?.DescendantNodes().OfType<ParameterSyntax>().ToList() ?? new List<ParameterSyntax>();
5 foreach (var parameterSyntax in properties)
6 {
7 var type = parameterSyntax?.Type?.ToString();
8 var name = parameterSyntax?.Identifier.Text;
9 if (type != null && name != null)
10 param.Add(new KeyValueModel(type, name));
11 }
12
13 var returnValue = methodDeclarationSyntax.ReturnType.ToString();
14
15 return new MethodMetaData(methodDeclarationSyntax.Identifier.Text,
16 methodDeclarationSyntax.GetAttributeMetaData(), returnValue, param, methodDeclarationSyntax.Modifiers.ToString());
17 }

4.8、一顿操作猛如虎,现在我们获取到了所有的信息,可以开干了。这一步处理元数据,过滤出需要生成代理类的信息。

约定一些规则:

就近原则类方法上的标签 > 类上的标签 > 接口方法上的标签 > 接口上的标签,即离实际的方法越近,优先级越高。

忽略Aop:打上 [IgnoreAop] 标签

管道模式:如果一个方法打上多个Attribute,则按照管道的原则,先进后出,注意,只有最接近方法的 Attribute 才能调用 Next 方法。如果有 三个 Attribute,分别是 attribute1、attribute2、attribute3,则执行顺序是 attribute1.Before => attribute2.Before => attribute3.Before => attribute3.Next => attribute3.After => attribute2.After => attribute1.After

按照这个约定,过滤得到需要的数据

        public List<AopCodeBuilderMetaData> GetAopCodeBuilderMetaData()
{
//就近原则,方法 > 类 > 接口方法 > 接口 var list = new List<AopCodeBuilderMetaData>();
foreach (var classMetaData in ClassMetaDataList.Where(d => !AopAttributeList.Contains(d.Name)))
{
////必须要可重写方法 放出错误
//if (classMetaData.MethodMetaData.All(d => !d.CanOverride))
// continue; var methods = new List<MethodMetaData>();
var classHasIgnore = classMetaData.HasIgnore(IgnoreAttribute); //实现的接口
classMetaData.Usings.Add(classMetaData.NameSpace);
classMetaData.InterfaceMetaData = InterfaceMetaDataList.Where(d => classMetaData.Interfaces.Contains(d.Key)
|| classMetaData.Interfaces.SelectMany(t => classMetaData.Usings.Select(u => $"{u.Replace("using ", "").Replace(";", "")}.{t.Split('.').Last()}")).Contains(d.Key)).ToList();
classMetaData.Usings.Remove(classMetaData.NameSpace); //按照就近原则过滤
//foreach (var methodMetaData in classMetaData.MethodMetaData.Where(d => d.CanOverride))
foreach (var methodMetaData in classMetaData.MethodMetaData)
{
//忽略
if (methodMetaData.AttributeMetaData.HasIgnore(IgnoreAttribute))
continue; //类方法标记
var methodAttrs = methodMetaData.AttributeMetaData.GetAopAttributes(AopAttributeList);
if (methodAttrs.Any())
{
methodMetaData.AttributeMetaData.Clear();
methodMetaData.AttributeMetaData.AddRange(methodAttrs);
methods.Add(methodMetaData);
continue;
} //类标记
if (classHasIgnore)
continue; var classAttr = classMetaData.AttributeMetaData.GetAopAttribute(AopAttributeList);
if (classAttr != null)
{
methodMetaData.AttributeMetaData.Clear();
methodMetaData.AttributeMetaData.Add(classAttr);
methods.Add(methodMetaData);
continue;
} //接口标记
if (!classMetaData.Interfaces.Any())
continue; //接口方法忽略
if (classMetaData.InterfaceMetaData.Any(d => d.MethodMetaData.FirstOrDefault(m => m.Key == methodMetaData.Key)?.AttributeMetaData.HasIgnore(IgnoreAttribute) == true))
continue; //接口方法标记
var interfaceMethodAttr = classMetaData.InterfaceMetaData.Select(d => d.MethodMetaData.FirstOrDefault(m => m.Key == methodMetaData.Key)?.AttributeMetaData.GetAopAttribute(AopAttributeList))
.FirstOrDefault(d => d != null); if (interfaceMethodAttr != null)
{
methodMetaData.AttributeMetaData.Clear();
methodMetaData.AttributeMetaData.Add(interfaceMethodAttr);
methods.Add(methodMetaData);
continue;
} //接口标记
var interfaceAttr = classMetaData.InterfaceMetaData.Where(d => d.MethodMetaData.Any(d => d.Key == methodMetaData.Key)).Select(d => d.AttributeMetaData.GetAopAttribute(AopAttributeList))
.FirstOrDefault(d => d != null);
if (interfaceAttr != null)
{
methodMetaData.AttributeMetaData.Clear();
methodMetaData.AttributeMetaData.Add(interfaceAttr);
methods.Add(methodMetaData);
continue;
}
} if (methods.Any())
list.Add(new AopCodeBuilderMetaData(classMetaData.NameSpace, classMetaData.Name, methods, classMetaData.Constructor, classMetaData.Usings, classMetaData.InterfaceMetaData));
} return list;
}

4.9、生成代码,生成 3.3 这样的代码。这一步就是代码拼接,StringBuilder 一把梭,需要注意的是处理不同的情况如 同步异步、有无返回值、方法的重载、拦截器的传值等。代码太原始不宜展示,感兴趣的可以去看源码。整个过程到此结束。

5、不服跑个分(bushi)

加上aop标签之后,整个方法调用链是 aopbefore => aopnext => 执行实际的方法 => aopafter,一共4层,每多一层,耗时就增加,在我的电脑上跑了一下,每增加一层调用,大概增加 20~30ns 的耗时。因此根据实际使用场景,增加了 HasBefore、HasAfter、HasAopNext、HasActualNext 这4个判断去自定义需要执行的方法。详情见1。

缓存场景:有缓存,直接 before 里获取缓存,直接返回,不需要后续的执行,此时只有before;无缓存:可以在 AopNext 中执行 ActualNext,更新缓存然后返回,或者 执行  ActualNext ,最后在 After 中更新缓存,这里可以省略一个方法调用;

业务日志:只需要执行 ActualNext,然后在 After 中写日志,这场景只有2个方法,节省2个方法,美滋滋。

以直接调用同步方法为基准,36ns

直接调用同步方法:1

直接调用异步方法:2.08

缓存场景同步调用:5.47

缓存场景异步调用:7.45

4个方法火力全开:同步:3.8

4个方法火力全开:异步:13.5

代码中使用了.net core 自带的DI 获取对象,有 几十ns 的开销。

6、结尾

SourceGenerator是个好东西,我司在用的场景还有生成一些额外属性,比如 给Dto中的 枚举、字典、行政区划等自动添加 Name 属性,不用手动一个个去处理,延长键盘使用寿命。

在这里提供了一些思路,你们用来做什么呢?

本文代码传送门:https://github.com/ad313/mic

另外分享一些SourceGenerator的项目:

https://github.com/amis92/csharp-source-generators

https://github.com/Cysharp/MemoryPack

SourceGenerator 使用姿势(1):生成代理类,实现简单的AOP的相关教程结束。