SourceGenerator 使用姿势(1):生成代理类,实现简单的AOP
SourceGenerator 已经出来很久了,也一直在关注。之前观摩大佬 xljiulang 的 WebApiClient 使用 SourceGenerator 生成接口代理类,深受启发,准备拿过来用看看(发出白嫖的声音),写个编译期静态代理AOP。本篇重点是怎么获取元数据,得到想要的数据,生成想要的代码(往下拖到第 4 点)。
几个月前写了个demo,现在趁着有空重新整理完善了下。.net 6 新增了个 IIncrementalGenerator 进行增量编译,这个还没研究,后面再说。
我的思路是继承,生成一个类去继承需要拦截的实际类,然后重写相关的方法,此时插入额外的方法,比如 Before,After 等。这就要求相关方法必须是 可重写 的, virtual 或 override。好了,开干。
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、不服跑个分
加上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