SourceGenerator 已经出来很久了,也一直在关注。之前观摩大佬 xljiulang 的 WebApiClient 使用 SourceGenerator 生成接口代理类,深受启发,准备拿过来用看看(发出白嫖的声音),写个编译期静态代理AOP。本篇重点是怎么获取元数据,得到想要的数据,生成想要的代码(往下拖到第 4 点)。
几个月前写了个demo,现在趁着有空重新整理完善了下。.net 6 新增了个 IIncrementalGenerator 进行增量编译,这个还没研究,后面再说。
我的思路是继承,生成一个类去继承需要拦截的实际类,然后重写相关的方法,此时插入额外的方法,比如 Before,After 等。这就要求相关方法必须是 可重写 的, virtual 或 override。好了,开干。
1、定义Aop属性,打个标签,SourceGenerator 根据这个标签查找相关的 class 或 interface
1 ///
4 public interface IAopInterceptor
5 {
6 ///
9 ///
10 ///
11 AopContext Before(AopContext context);
12 ///
15 ///
16 ///
17 ValueTask
18 ///
21 ///
22 ///
23 AopContext After(AopContext context);
24 ///
27 ///
28 ///
29 ValueTask
30 ///
33 ///
34 ///
35 AopContext Next(AopContext context);
36 ///
39 ///
40 ///
41 ValueTask
42 }
可以不要 IAopInterceptor 这个接口,这里加了只是为了约束。
1 public class AopInterceptor : Attribute, IAopInterceptor
2 {
3 ///
6 public bool HasBefore { get; set; }
7 ///
10 public bool HasAfter { get; set; }
11 ///
14 public bool HasAopNext { get; set; }
15 ///
18 public bool HasActualNext { get; set; }
19
20 ///
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
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
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
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 ///
4 public struct AopContext
5 {
6 ///
9 public bool IsTask { get; private set; }
10 ///
13 public bool HasReturnValue { get; private set; }
14 ///
17 public Dictionary
18
19 ///
22 public Func
23 ///
26 public dynamic ReturnValue { get; set; }
27 ///
30 public Exception Exception { get; set; }
31 ///
34 public IServiceProvider ServiceProvider { get; private set; }
35
36 ///
39 ///
40 ///
41 ///
42 ///
43 ///
44 public AopContext(IServiceProvider serviceProvider, Dictionary
45 {
46 ServiceProvider = serviceProvider;
47 MethodInputParam = methodInputParam;
48 IsTask = isTask;
49 HasReturnValue = hasReturnValue;
50 ActualMethod = actualMethod;
51 }
52
53 ///
56 ///
57 public async ValueTask
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 ///
75 ///
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 ///
4 public class SampleAttribute : AopInterceptor
5 {
6 ///
7 ///
8 ///
9 public override AopContext Before(AopContext context)
10 {
11 Console.WriteLine("Before…");
12 return base.Before(context);
13 }
14
15 ///
16 ///
17 ///
18 public override ValueTask
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 ///
31 ///
32 ///
33 public override ValueTask
34 {
35 Console.WriteLine("AfterAsync…");
36 return base.AfterAsync(context);
37 }
38
39 ///
40 ///
41 ///
42 public override AopContext Next(AopContext context)
43 {
44 Console.WriteLine("Next…");
45 return base.Next(context);
46 }
47
48 ///
49 ///
50 ///
51 public override ValueTask
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
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
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
13 false,
14 true,
15 null);
16
17 var aopInterceptor0 = _serviceProvider0.GetRequiredService
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
40 {
41 var aopContext = new AopContext(_serviceProvider0,
42 new Dictionary
43 true,
44 true,
45 null);
46
47 var aopInterceptor0 = _serviceProvider0.GetRequiredService
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,引入两个分析器包
4.2、新建类 AopGenerator,继承 ISourceGenerator 接口,实现 Execute 方法,Execute 的内容是最终的成品。
1 ///
4 [Generator]
5 public class AopGenerator : ISourceGenerator
6 {
7 ///
10 ///
11 public void Initialize(GeneratorInitializationContext context)
12 {
13 //Debugger.Launch();
14
15 context.RegisterForSyntaxNotifications(() => new AopSyntaxReceiver());
16 }
17
18 ///
21 ///
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 ///
4 ///
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
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 ///
4 ///
5 ///
6 public AopMetaData GetAopMetaData(Compilation compilation)
7 {
8 var result = new AopMetaData(AopAttributeList, IgnoreAttribute, new List
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
20 .ToList();
21
22 if (!interfaceWithAttribute.Any())
23 continue;
24
25 //处理接口
26 foreach (var interfaceDeclaration in interfaceWithAttribute)
27 {
28 var namespaceName = interfaceDeclaration.FindParent
29 var className = interfaceDeclaration.Identifier.Text;
30 var properties = interfaceDeclaration.DescendantNodes().OfType
31 var methodSyntaxs = interfaceDeclaration.DescendantNodes().OfType
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
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
4 var className = classDeclaration.Identifier.Text;
5 var properties = classDeclaration.DescendantNodes().OfType
6 var methodSyntaxs = classDeclaration.DescendantNodes().OfType
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
14 //using 引用
15 var usingDirectiveSyntax = classDeclaration.Parent?.Parent == null ? new SyntaxList
16 var usings = usingDirectiveSyntax.Select(d => d.ToString()).ToList();
17
18 //构造函数
19 var constructorDictionary = new List
20 foreach (var memberDeclarationSyntax in classDeclaration.Members)
21 {
22 if (memberDeclarationSyntax.Kind().ToString() == "ConstructorDeclaration")
23 {
24 //constructorDictionary = memberDeclarationSyntax.DescendantNodes().OfType
25 constructorDictionary = memberDeclarationSyntax.DescendantNodes().OfType
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
4 var properties = methodDeclarationSyntax.DescendantNodes().OfType
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的项目:
手机扫一扫
移动阅读更方便
你可能感兴趣的文章