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

打印 上一主题 下一主题

主题 848|帖子 848|积分 2544

SourceGenerator 已经出来很久了,也一直在关注。之前观摩大佬 xljiulang 的 WebApiClient 使用 SourceGenerator 生成接口代理类,深受启发,准备拿过来用看看(发出白嫖的声音),写个编译期静态代理AOP。本篇重点是怎么获取元数据,得到想要的数据,生成想要的代码(往下拖到第 4 点)。
几个月前写了个demo,现在趁着有空重新整理完善了下。.net 6 新增了个 IIncrementalGenerator 进行增量编译,这个还没研究,后面再说。
我的思路是继承,生成一个类去继承需要拦截的实际类,然后重写相关的方法,此时插入额外的方法,比如 Before,After 等。这就要求相关方法必须是 可重写 的, virtualoverride。好了,开干。
1、定义Aop属性,打个标签,SourceGenerator 根据这个标签查找相关的 class 或 interface
  1. 1     /// <summary>
  2. 2     /// Aop 拦截器
  3. 3     /// </summary>
  4. 4     public interface IAopInterceptor
  5. 5     {
  6. 6         /// <summary>
  7. 7         /// 执行前操作,同步方法调用
  8. 8         /// </summary>
  9. 9         /// <param name="context"></param>
  10. 10         /// <returns></returns>
  11. 11         AopContext Before(AopContext context);
  12. 12         /// <summary>
  13. 13         /// 执行前操作,异步方法调用
  14. 14         /// </summary>
  15. 15         /// <param name="context"></param>
  16. 16         /// <returns></returns>
  17. 17         ValueTask<AopContext> BeforeAsync(AopContext context);
  18. 18         /// <summary>
  19. 19         /// 执行后操作,同步方法调用
  20. 20         /// </summary>
  21. 21         /// <param name="context"></param>
  22. 22         /// <returns></returns>
  23. 23         AopContext After(AopContext context);
  24. 24         /// <summary>
  25. 25         /// 执行后操作,异步方法调用
  26. 26         /// </summary>
  27. 27         /// <param name="context"></param>
  28. 28         /// <returns></returns>
  29. 29         ValueTask<AopContext> AfterAsync(AopContext context);
  30. 30         /// <summary>
  31. 31         /// 执行方法,同步方法调用
  32. 32         /// </summary>
  33. 33         /// <param name="context"></param>
  34. 34         /// <returns></returns>
  35. 35         AopContext Next(AopContext context);
  36. 36         /// <summary>
  37. 37         /// 执行方法,异步方法调用
  38. 38         /// </summary>
  39. 39         /// <param name="context"></param>
  40. 40         /// <returns></returns>
  41. 41         ValueTask<AopContext> NextAsync(AopContext context);
  42. 42     }
复制代码
可以不要 IAopInterceptor 这个接口,这里加了只是为了约束。
  1. 1     public class AopInterceptor : Attribute, IAopInterceptor
  2. 2     {
  3. 3         /// <summary>
  4. 4         /// 是否执行 Before
  5. 5         /// </summary>
  6. 6         public bool HasBefore { get; set; }
  7. 7         /// <summary>
  8. 8         /// 是否执行 After
  9. 9         /// </summary>
  10. 10         public bool HasAfter { get; set; }
  11. 11         /// <summary>
  12. 12         /// 是否执行 Aop 的 Next
  13. 13         /// </summary>
  14. 14         public bool HasAopNext { get; set; }
  15. 15         /// <summary>
  16. 16         /// 是否执行实际的方法
  17. 17         /// </summary>
  18. 18         public bool HasActualNext { get; set; }
  19. 19
  20. 20         /// <summary>
  21. 21         /// 默认执行所以方法
  22. 22         /// </summary>
  23. 23         public AopInterceptor()
  24. 24         {
  25. 25             HasBefore = true;
  26. 26             HasAopNext = true;
  27. 27             HasActualNext = true;
  28. 28             HasAfter = true;
  29. 29         }
  30. 30
  31. 31         public virtual AopContext Before(AopContext context) => context;
  32. 32
  33. 33         public virtual async ValueTask<AopContext> BeforeAsync(AopContext context)
  34. 34         {
  35. 35             await ValueTask.CompletedTask;
  36. 36             return context;
  37. 37         }
  38. 38
  39. 39         public virtual AopContext After(AopContext context)
  40. 40         {
  41. 41             return context.Exception != null ? throw context.Exception : context;
  42. 42         }
  43. 43
  44. 44         public virtual async ValueTask<AopContext> AfterAsync(AopContext context)
  45. 45         {
  46. 46             if (context.Exception != null)
  47. 47                 throw context.Exception;
  48. 48
  49. 49             await ValueTask.CompletedTask;
  50. 50             return context;
  51. 51         }
  52. 52
  53. 53         public virtual AopContext Next(AopContext context)
  54. 54         {
  55. 55             try
  56. 56             {
  57. 57                 context.Invoke();
  58. 58             }
  59. 59             catch (Exception e)
  60. 60             {
  61. 61                 context.Exception = e;
  62. 62             }
  63. 63             return context;
  64. 64         }
  65. 65
  66. 66         public virtual async ValueTask<AopContext> NextAsync(AopContext context)
  67. 67         {
  68. 68             try
  69. 69             {
  70. 70                 context = await context.InvokeAsync();
  71. 71             }
  72. 72             catch (Exception e)
  73. 73             {
  74. 74                 context.Exception = e;
  75. 75             }
  76. 76
  77. 77             return context;
  78. 78         }
  79. 79     }
复制代码
View Code 
2、定义上下文,主要包含 是否是异步,是否有返回值,还有实际方法的委托。决定了调用实际方法的时候怎么调用
  1. 1     /// <summary>
  2. 2     /// Aop 上下文
  3. 3     /// </summary>
  4. 4     public struct AopContext
  5. 5     {
  6. 6         /// <summary>
  7. 7         /// 是否是异步
  8. 8         /// </summary>
  9. 9         public bool IsTask { get; private set; }
  10. 10         /// <summary>
  11. 11         /// 是否有返回值
  12. 12         /// </summary>
  13. 13         public bool HasReturnValue { get; private set; }
  14. 14         /// <summary>
  15. 15         /// 方法输入参数
  16. 16         /// </summary>
  17. 17         public Dictionary<string, dynamic> MethodInputParam { get; private set; }
  18. 18
  19. 19         /// <summary>
  20. 20         /// 实际方法执行结果,可能是 Task
  21. 21         /// </summary>
  22. 22         public Func<dynamic> ActualMethod { get; set; }
  23. 23         /// <summary>
  24. 24         /// 返回值,具体的值
  25. 25         /// </summary>
  26. 26         public dynamic ReturnValue { get; set; }
  27. 27         /// <summary>
  28. 28         /// 异常信息
  29. 29         /// </summary>
  30. 30         public Exception Exception { get; set; }
  31. 31         /// <summary>
  32. 32         /// IServiceProvider
  33. 33         /// </summary>
  34. 34         public IServiceProvider ServiceProvider { get; private set; }
  35. 35
  36. 36         /// <summary>
  37. 37         /// 初始化
  38. 38         /// </summary>
  39. 39         /// <param name="serviceProvider"></param>
  40. 40         /// <param name="methodInputParam"></param>
  41. 41         /// <param name="isTask"></param>
  42. 42         /// <param name="hasReturnValue"></param>
  43. 43         /// <param name="actualMethod"></param>
  44. 44         public AopContext(IServiceProvider serviceProvider, Dictionary<string, dynamic> methodInputParam, bool isTask, bool hasReturnValue, Func<dynamic> actualMethod) : this()
  45. 45         {
  46. 46             ServiceProvider = serviceProvider;
  47. 47             MethodInputParam = methodInputParam;
  48. 48             IsTask = isTask;
  49. 49             HasReturnValue = hasReturnValue;
  50. 50             ActualMethod = actualMethod;
  51. 51         }
  52. 52
  53. 53         /// <summary>
  54. 54         /// 执行实际方法 异步
  55. 55         /// </summary>
  56. 56         /// <returns></returns>
  57. 57         public async ValueTask<AopContext> InvokeAsync()
  58. 58         {
  59. 59             if (ActualMethod == null)
  60. 60                 return this;
  61. 61
  62. 62             if (HasReturnValue)
  63. 63             {
  64. 64                 ReturnValue = await ActualMethod();
  65. 65                 return this;
  66. 66             }
  67. 67
  68. 68             await ActualMethod();
  69. 69             return this;
  70. 70         }
  71. 71
  72. 72         /// <summary>
  73. 73         /// 执行实际方法 同步
  74. 74         /// </summary>
  75. 75         /// <returns></returns>
  76. 76         public void Invoke()
  77. 77         {
  78. 78             if (ActualMethod == null)
  79. 79                 return;
  80. 80
  81. 81             //特殊处理 同步且没有返回值,用 Task.Run 包装
  82. 82             if (!IsTask && !HasReturnValue)
  83. 83                 ActualMethod.Invoke().GetAwaiter().GetResult();
  84. 84             else
  85. 85                 ReturnValue = ActualMethod.Invoke();
  86. 86         }
  87. 87     }
复制代码
3、硬编码实现类
3.1、定义拦截器
  1. 1     /// <summary>
  2. 2     /// 常规服务,执行所有方法
  3. 3     /// </summary>
  4. 4     public class SampleAttribute : AopInterceptor
  5. 5     {
  6. 6         /// <summary>执行前操作,同步方法调用</summary>
  7. 7         /// <param name="context"></param>
  8. 8         /// <returns></returns>
  9. 9         public override AopContext Before(AopContext context)
  10. 10         {
  11. 11             Console.WriteLine("Before...");
  12. 12             return base.Before(context);
  13. 13         }
  14. 14
  15. 15         /// <summary>执行前操作,异步方法调用</summary>
  16. 16         /// <param name="context"></param>
  17. 17         /// <returns></returns>
  18. 18         public override ValueTask<AopContext> BeforeAsync(AopContext context)
  19. 19         {
  20. 20             Console.WriteLine("BeforeAsync...");
  21. 21             return base.BeforeAsync(context);
  22. 22         }
  23. 23
  24. 24         public override AopContext After(AopContext context)
  25. 25         {
  26. 26             Console.WriteLine("After...");
  27. 27             return context;
  28. 28         }
  29. 29
  30. 30         /// <summary>执行后操作,异步方法调用</summary>
  31. 31         /// <param name="context"></param>
  32. 32         /// <returns></returns>
  33. 33         public override ValueTask<AopContext> AfterAsync(AopContext context)
  34. 34         {
  35. 35             Console.WriteLine("AfterAsync...");
  36. 36             return base.AfterAsync(context);
  37. 37         }
  38. 38
  39. 39         /// <summary>执行方法,同步方法调用</summary>
  40. 40         /// <param name="context"></param>
  41. 41         /// <returns></returns>
  42. 42         public override AopContext Next(AopContext context)
  43. 43         {
  44. 44             Console.WriteLine("Next...");
  45. 45             return base.Next(context);
  46. 46         }
  47. 47
  48. 48         /// <summary>执行方法,异步方法调用</summary>
  49. 49         /// <param name="context"></param>
  50. 50         /// <returns></returns>
  51. 51         public override ValueTask<AopContext> NextAsync(AopContext context)
  52. 52         {
  53. 53             Console.WriteLine("NextAsync...");
  54. 54             return base.NextAsync(context);
  55. 55         }
  56. 56     }
复制代码
View Code定义接口
  1. 1     public interface ITestService
  2. 2     {
  3. 3         [Sample]
  4. 4         DateTime SampleSync();
  5. 5
  6. 6         [Sample]
  7. 7         ValueTask<DateTime> SampleAsync();
  8. 8     }
复制代码
 
3.2、定义实现类
  1. 1     public class TestService : ITestService
  2. 2     {
  3. 3
  4. 4         public virtual DateTime SampleSync()
  5. 5         {
  6. 6             return DateTime.Now;
  7. 7         }
  8. 8
  9. 9         public virtual async ValueTask<DateTime> SampleAsync()
  10. 10         {
  11. 11             await ValueTask.CompletedTask;
  12. 12             return DateTime.Now;
  13. 13         }
  14. 14     }
复制代码
 
3.3、定义继承类,重写相关方法
  1. 1     public sealed class TestService_Aop : TestService
  2. 2     {
  3. 3         private readonly IServiceProvider _serviceProvider0;
  4. 4         public TestService_Aop(IServiceProvider serviceProvider0)
  5. 5         {
  6. 6             _serviceProvider0 = serviceProvider0;
  7. 7         }
  8. 8
  9. 9         public override DateTime SampleSync()
  10. 10         {
  11. 11             var aopContext = new AopContext(_serviceProvider0,
  12. 12                 new Dictionary<string, dynamic>() { },
  13. 13                 false,
  14. 14                 true,
  15. 15                 null);
  16. 16
  17. 17             var aopInterceptor0 = _serviceProvider0.GetRequiredService<SampleAttribute>();
  18. 18             if (aopInterceptor0.HasBefore) aopContext = aopInterceptor0.Before(aopContext);
  19. 19             if (aopInterceptor0.HasAopNext)
  20. 20             {
  21. 21                 if (aopInterceptor0.HasActualNext)
  22. 22                 {
  23. 23                     aopContext.ActualMethod = () => base.SampleSync();
  24. 24                 }
  25. 25                 aopContext = aopInterceptor0.Next(aopContext);
  26. 26             }
  27. 27             else
  28. 28             {
  29. 29                 if (aopInterceptor0.HasActualNext)
  30. 30                 {
  31. 31                     aopContext.ReturnValue = base.SampleSync();
  32. 32                 }
  33. 33             }
  34. 34             if (aopInterceptor0.HasAfter) aopContext = aopInterceptor0.After(aopContext);
  35. 35
  36. 36             return aopContext.ReturnValue;
  37. 37         }
  38. 38
  39. 39         public override async ValueTask<DateTime> SampleAsync()
  40. 40         {
  41. 41             var aopContext = new AopContext(_serviceProvider0,
  42. 42                 new Dictionary<string, dynamic>() { },
  43. 43                 true,
  44. 44                 true,
  45. 45                 null);
  46. 46
  47. 47             var aopInterceptor0 = _serviceProvider0.GetRequiredService<SampleAttribute>();
  48. 48             if (aopInterceptor0.HasBefore) aopContext = await aopInterceptor0.BeforeAsync(aopContext);
  49. 49             if (aopInterceptor0.HasAopNext)
  50. 50             {
  51. 51                 if (aopInterceptor0.HasActualNext)
  52. 52                 {
  53. 53                     aopContext.ActualMethod = () => base.SampleAsync();
  54. 54                 }
  55. 55                 aopContext = await aopInterceptor0.NextAsync(aopContext);
  56. 56             }
  57. 57             else
  58. 58             {
  59. 59                 if (aopInterceptor0.HasActualNext)
  60. 60                 {
  61. 61                     aopContext.ReturnValue = await base.SampleAsync();
  62. 62                 }
  63. 63             }
  64. 64             if (aopInterceptor0.HasAfter) aopContext = await aopInterceptor0.AfterAsync(aopContext);
  65. 65
  66. 66             return aopContext.ReturnValue;
  67. 67         }
  68. 68     }
复制代码
 
4、开整
4.1、新建项目 Mic.Aop.Generator,TargetFramework 选 netstandard2.0,引入两个分析器包
  1. <ItemGroup>
  2.         <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" PrivateAssets="all" />
  3.         <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
  4.             <PrivateAssets>all</PrivateAssets>
  5.             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
  6.         </PackageReference>
  7.     </ItemGroup>
复制代码
 
4.2、新建类 AopGenerator,继承 ISourceGenerator 接口,实现 Execute 方法,Execute 的内容是最终的成品。
  1. 1     /// <summary>
  2. 2     /// 代码生成器
  3. 3     /// </summary>
  4. 4     [Generator]
  5. 5     public class AopGenerator : ISourceGenerator
  6. 6     {
  7. 7         /// <summary>
  8. 8         /// 初始化
  9. 9         /// </summary>
  10. 10         /// <param name="context"></param>
  11. 11         public void Initialize(GeneratorInitializationContext context)
  12. 12         {
  13. 13             //Debugger.Launch();
  14. 14
  15. 15             context.RegisterForSyntaxNotifications(() => new AopSyntaxReceiver());
  16. 16         }
  17. 17
  18. 18         /// <summary>
  19. 19         /// 执行
  20. 20         /// </summary>
  21. 21         /// <param name="context"></param>
  22. 22         public void Execute(GeneratorExecutionContext context)
  23. 23         {
  24. 24             if (context.SyntaxReceiver is AopSyntaxReceiver receiver)
  25. 25             {
  26. 26                 var aopMateData = receiver
  27. 27                     .FindAopInterceptor() // 查找所有的拦截器
  28. 28                     .GetAopMetaData(context.Compilation); //根据拦截器找到所有的类或方法,获取元数据,包含所有接口、实现类、所有属性、所有方法
  29. 29
  30. 30                 var builders = aopMateData
  31. 31                     .GetAopCodeBuilderMetaData()  //获取用于构建代码的元数据,过滤出需要的数据
  32. 32                     .Select(i => new AopCodeBuilder(i))
  33. 33                     .Distinct()
  34. 34                     .ToList();
  35. 35                 //开始生成代码
  36. 36                 foreach (var builder in builders)
  37. 37                 {
  38. 38                     context.AddSource(builder.SourceCodeName, builder.ToSourceText());
  39. 39                 }
  40. 40             }
  41. 41         }
  42. 42     }
复制代码
 
4.3、AopSyntaxReceiver 语法树处理类,这一步获取到所有的数据:接口、类、属性、方法、参数等等等
  1.     /// <summary>
  2.     /// 语法接收器
  3.     /// </summary>
  4.     sealed class AopSyntaxReceiver : ISyntaxReceiver
  5.     {
  6.         private const string GeneratorTagName = "AopInterceptor"; //所有拦截器需要继承的基类
  7.         private const string IgnoreAttribute = "IgnoreAopAttribute"; //忽略aop
  8.         /// <summary>
  9.         /// 类列表
  10.         /// </summary>
  11.         private readonly List<ClassDeclarationSyntax> _classSyntaxList = new List<ClassDeclarationSyntax>();
  12.         /// <summary>
  13.         /// 接口列表
  14.         /// </summary>
  15.         private readonly List<InterfaceDeclarationSyntax> _interfaceSyntaxList = new List<InterfaceDeclarationSyntax>();
  16.         /// <summary>
  17.         /// 所有的AopInterceptor
  18.         /// </summary>
  19.         public List<string> AopAttributeList = new List<string>();
  20.         /// <summary>
  21.         /// 所有的AopInterceptor
  22.         /// </summary>
  23.         public List<ClassMetaData> AopAttributeClassMetaDataList = new List<ClassMetaData>();
  24.         /// <summary>
  25.         /// 访问语法树
  26.         /// </summary>
  27.         /// <param name="syntaxNode"></param>
  28.         void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode)
  29.         {
  30.             if (syntaxNode is InterfaceDeclarationSyntax interfaceSyntax)
  31.             {
  32.                 this._interfaceSyntaxList.Add(interfaceSyntax);
  33.             }
  34.             if (syntaxNode is ClassDeclarationSyntax classSyntax)
  35.             {
  36.                 this._classSyntaxList.Add(classSyntax);
  37.             }
  38.         }
  39.         
  40.         //其他代码........
  41.     }
复制代码
 
4.4、找到所有的拦截器
  1. 1         /// <summary>
  2. 2         /// 找出所有 AopInterceptor
  3. 3         /// </summary>
  4. 4         /// <returns></returns>
  5. 5         public AopSyntaxReceiver FindAopInterceptor()
  6. 6         {
  7. 7             foreach (var classSyntax in this._classSyntaxList)
  8. 8             {
  9. 9                 var root = classSyntax.SyntaxTree.GetRoot();
  10. 10                 var classesWithAttribute = root
  11. 11                     .DescendantNodes()
  12. 12                     .OfType<ClassDeclarationSyntax>()
  13. 13                     .ToList();
  14. 14
  15. 15                 if (!classesWithAttribute.Any())
  16. 16                     continue;
  17. 17
  18. 18                 foreach (var classDeclarationSyntax in classesWithAttribute)
  19. 19                 {
  20. 20                     if (classDeclarationSyntax.BaseList == null)
  21. 21                         continue;
  22. 22
  23. 23                     foreach (BaseTypeSyntax baseTypeSyntax in classDeclarationSyntax.BaseList.Types)
  24. 24                     {
  25. 25                         if (baseTypeSyntax.ToString().Trim() == GeneratorTagName)
  26. 26                         {
  27. 27                             AopAttributeList.Add(classDeclarationSyntax.Identifier.Text);
  28. 28
  29. 29                             var meta = GetClassMetaData(classSyntax);
  30. 30                             if (meta != null && AopAttributeClassMetaDataList.All(d => d.Name != meta.Name))
  31. 31                                 AopAttributeClassMetaDataList.Add(meta);
  32. 32                         }
  33. 33                     }
  34. 34                 }
  35. 35             }
  36. 36
  37. 37             AopAttributeList = AopAttributeList.Distinct().ToList();
  38. 38
  39. 39             return this;
  40. 40         }
复制代码
 
4.5、找到所有接口和打了标记的class
  1. 1         /// <summary>
  2. 2         /// 获取所有打了标记的接口和类
  3. 3         /// </summary>
  4. 4         /// <param name="compilation"></param>
  5. 5         /// <returns></returns>
  6. 6         public AopMetaData GetAopMetaData(Compilation compilation)
  7. 7         {
  8. 8             var result = new AopMetaData(AopAttributeList, IgnoreAttribute, new List<InterfaceMetaData>(), new List<ClassMetaData>());
  9. 9
  10. 10             if (!AopAttributeList.Any())
  11. 11                 return result;
  12. 12
  13. 13             //处理接口
  14. 14             foreach (var classSyntax in this._interfaceSyntaxList)
  15. 15             {
  16. 16                 var root = classSyntax.SyntaxTree.GetRoot();
  17. 17                 var interfaceWithAttribute = root
  18. 18                     .DescendantNodes()
  19. 19                     .OfType<InterfaceDeclarationSyntax>()
  20. 20                     .ToList();
  21. 21
  22. 22                 if (!interfaceWithAttribute.Any())
  23. 23                     continue;
  24. 24
  25. 25                 //处理接口
  26. 26                 foreach (var interfaceDeclaration in interfaceWithAttribute)
  27. 27                 {
  28. 28                     var namespaceName = interfaceDeclaration.FindParent<NamespaceDeclarationSyntax>().Name.ToString();
  29. 29                     var className = interfaceDeclaration.Identifier.Text;
  30. 30                     var properties = interfaceDeclaration.DescendantNodes().OfType<PropertyDeclarationSyntax>().ToList();
  31. 31                     var methodSyntaxs = interfaceDeclaration.DescendantNodes().OfType<MethodDeclarationSyntax>().ToList();
  32. 32                     
  33. 33                     //属性集合
  34. 34                     var props = properties.Select(d => new PropertyMetaData(d.Identifier.Text, d.GetAttributeMetaData())).ToList();
  35. 35                     //方法集合
  36. 36                     var methods = methodSyntaxs.Select(GetMethodMetaData).ToList();
  37. 37
  38. 38                     var interfaceMetaData = new InterfaceMetaData(namespaceName, className, interfaceDeclaration.GetAttributeMetaData(), props, methods);
  39. 39                     if (interfaceMetaData.MethodMetaData.Any() && !result.InterfaceMetaDataList.Exists(d => d.Equals(interfaceMetaData)))
  40. 40                         result.InterfaceMetaDataList.Add(interfaceMetaData);
  41. 41                 }
  42. 42             }
  43. 43
  44. 44             //处理类
  45. 45             foreach (var classSyntax in this._classSyntaxList)
  46. 46             {
  47. 47                 var root = classSyntax.SyntaxTree.GetRoot();
  48. 48                 var classesWithAttribute = root
  49. 49                     .DescendantNodes()
  50. 50                     .OfType<ClassDeclarationSyntax>()
  51. 51                     .ToList();
  52. 52
  53. 53                 if (!classesWithAttribute.Any())
  54. 54                     continue;
  55. 55
  56. 56                 foreach (var classDeclaration in classesWithAttribute)
  57. 57                 {
  58. 58                     var classMetaData = GetClassMetaData(classDeclaration);
  59. 59                     if (classMetaData == null)
  60. 60                         continue;
  61. 61
  62. 62                     if (AopAttributeList.Contains(classMetaData.Name))
  63. 63                         continue;
  64. 64
  65. 65                     if (classMetaData.MethodMetaData.Any() && !result.ClassMetaDataList.Exists(d => d.Equals(classMetaData)))
  66. 66                         result.ClassMetaDataList.Add(classMetaData);
  67. 67                 }
  68. 68             }
  69. 69
  70. 70             result.AopAttributeClassMetaDataList = AopAttributeClassMetaDataList;
  71. 71
  72. 72             return result;
  73. 73         }
复制代码
 
4.6、获取 class 的信息:属性、方法集合、继承的接口、using引用、构造函数
  1. 1         private ClassMetaData? GetClassMetaData(ClassDeclarationSyntax classDeclaration)
  2. 2         {
  3. 3             var namespaceName = classDeclaration.FindParent<NamespaceDeclarationSyntax>().Name.ToString();
  4. 4             var className = classDeclaration.Identifier.Text;
  5. 5             var properties = classDeclaration.DescendantNodes().OfType<PropertyDeclarationSyntax>().ToList();
  6. 6             var methodSyntaxs = classDeclaration.DescendantNodes().OfType<MethodDeclarationSyntax>().ToList();
  7. 7
  8. 8             //属性集合
  9. 9             var props = properties.Select(d => new PropertyMetaData(d.Identifier.Text, d.GetAttributeMetaData())).ToList();
  10. 10             //方法集合
  11. 11             var methods = methodSyntaxs.Select(GetMethodMetaData).ToList();
  12. 12             //实现的接口集合
  13. 13             var interfaces = classDeclaration.BaseList?.ToString().Split(':').Last().Trim().Split(',').Where(d => d.Split('.').Last().StartsWith("I")).ToList() ?? new List<string>();
  14. 14             //using 引用
  15. 15             var usingDirectiveSyntax = classDeclaration.Parent?.Parent == null ? new SyntaxList<UsingDirectiveSyntax>() : ((CompilationUnitSyntax)classDeclaration.Parent.Parent).Usings;
  16. 16             var usings = usingDirectiveSyntax.Select(d => d.ToString()).ToList();
  17. 17
  18. 18             //构造函数
  19. 19             var constructorDictionary = new List<KeyValueModel>();
  20. 20             foreach (var memberDeclarationSyntax in classDeclaration.Members)
  21. 21             {
  22. 22                 if (memberDeclarationSyntax.Kind().ToString() == "ConstructorDeclaration")
  23. 23                 {
  24. 24                     //constructorDictionary = memberDeclarationSyntax.DescendantNodes().OfType<ParameterSyntax>().ToDictionary(d => d.GetFirstToken().Text, d => d.GetLastToken().Text);
  25. 25                     constructorDictionary = memberDeclarationSyntax.DescendantNodes().OfType<ParameterSyntax>().Select(d => new KeyValueModel(d.Type?.ToString(), d.Identifier.Text)).ToList();
  26. 26                     break;
  27. 27                 }
  28. 28             }
  29. 29
  30. 30             return new ClassMetaData(namespaceName, className, classDeclaration.GetAttributeMetaData(), props, methods, interfaces, constructorDictionary, usings);
  31. 31         }
复制代码
 
4.7、获取 method 的信息:方法名称、是否异步、是否有返回值、是否可重写、参数信息、Aop 标记集合(可能有多个)
  1. 1         private MethodMetaData GetMethodMetaData(MethodDeclarationSyntax methodDeclarationSyntax)
  2. 2         {
  3. 3             var param = new List<KeyValueModel>();
  4. 4             var properties = methodDeclarationSyntax.DescendantNodes().OfType<ParameterListSyntax>().FirstOrDefault()?.DescendantNodes().OfType<ParameterSyntax>().ToList() ?? new List<ParameterSyntax>();
  5. 5             foreach (var parameterSyntax in properties)
  6. 6             {
  7. 7                 var type = parameterSyntax?.Type?.ToString();
  8. 8                 var name = parameterSyntax?.Identifier.Text;
  9. 9                 if (type != null && name != null)
  10. 10                     param.Add(new KeyValueModel(type, name));
  11. 11             }
  12. 12
  13. 13             var returnValue = methodDeclarationSyntax.ReturnType.ToString();
  14. 14
  15. 15             return new MethodMetaData(methodDeclarationSyntax.Identifier.Text,
  16. 16                 methodDeclarationSyntax.GetAttributeMetaData(), returnValue, param, methodDeclarationSyntax.Modifiers.ToString());
  17. 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
按照这个约定,过滤得到需要的数据
  1.         public List<AopCodeBuilderMetaData> GetAopCodeBuilderMetaData()
  2.         {
  3.             //就近原则,方法 > 类 > 接口方法 > 接口
  4.             var list = new List<AopCodeBuilderMetaData>();
  5.             foreach (var classMetaData in ClassMetaDataList.Where(d => !AopAttributeList.Contains(d.Name)))
  6.             {
  7.                 ////必须要可重写方法 放出错误
  8.                 //if (classMetaData.MethodMetaData.All(d => !d.CanOverride))
  9.                 //    continue;
  10.                 var methods = new List<MethodMetaData>();
  11.                 var classHasIgnore = classMetaData.HasIgnore(IgnoreAttribute);
  12.                 //实现的接口
  13.                 classMetaData.Usings.Add(classMetaData.NameSpace);
  14.                 classMetaData.InterfaceMetaData = InterfaceMetaDataList.Where(d => classMetaData.Interfaces.Contains(d.Key)
  15.                     || classMetaData.Interfaces.SelectMany(t => classMetaData.Usings.Select(u => $"{u.Replace("using ", "").Replace(";", "")}.{t.Split('.').Last()}")).Contains(d.Key)).ToList();
  16.                 classMetaData.Usings.Remove(classMetaData.NameSpace);
  17.                 //按照就近原则过滤
  18.                 //foreach (var methodMetaData in classMetaData.MethodMetaData.Where(d => d.CanOverride))
  19.                 foreach (var methodMetaData in classMetaData.MethodMetaData)
  20.                 {
  21.                     //忽略
  22.                     if (methodMetaData.AttributeMetaData.HasIgnore(IgnoreAttribute))
  23.                         continue;
  24.                     //类方法标记
  25.                     var methodAttrs = methodMetaData.AttributeMetaData.GetAopAttributes(AopAttributeList);
  26.                     if (methodAttrs.Any())
  27.                     {
  28.                         methodMetaData.AttributeMetaData.Clear();
  29.                         methodMetaData.AttributeMetaData.AddRange(methodAttrs);
  30.                         methods.Add(methodMetaData);
  31.                         continue;
  32.                     }
  33.                     //类标记
  34.                     if (classHasIgnore)
  35.                         continue;
  36.                     var classAttr = classMetaData.AttributeMetaData.GetAopAttribute(AopAttributeList);
  37.                     if (classAttr != null)
  38.                     {
  39.                         methodMetaData.AttributeMetaData.Clear();
  40.                         methodMetaData.AttributeMetaData.Add(classAttr);
  41.                         methods.Add(methodMetaData);
  42.                         continue;
  43.                     }
  44.                     //接口标记
  45.                     if (!classMetaData.Interfaces.Any())
  46.                         continue;
  47.                     //接口方法忽略
  48.                     if (classMetaData.InterfaceMetaData.Any(d => d.MethodMetaData.FirstOrDefault(m => m.Key == methodMetaData.Key)?.AttributeMetaData.HasIgnore(IgnoreAttribute) == true))
  49.                         continue;
  50.                     //接口方法标记
  51.                     var interfaceMethodAttr = classMetaData.InterfaceMetaData.Select(d => d.MethodMetaData.FirstOrDefault(m => m.Key == methodMetaData.Key)?.AttributeMetaData.GetAopAttribute(AopAttributeList))
  52.                         .FirstOrDefault(d => d != null);
  53.                     if (interfaceMethodAttr != null)
  54.                     {
  55.                         methodMetaData.AttributeMetaData.Clear();
  56.                         methodMetaData.AttributeMetaData.Add(interfaceMethodAttr);
  57.                         methods.Add(methodMetaData);
  58.                         continue;
  59.                     }
  60.                     //接口标记
  61.                     var interfaceAttr = classMetaData.InterfaceMetaData.Where(d => d.MethodMetaData.Any(d => d.Key == methodMetaData.Key)).Select(d => d.AttributeMetaData.GetAopAttribute(AopAttributeList))
  62.                         .FirstOrDefault(d => d != null);
  63.                     if (interfaceAttr != null)
  64.                     {
  65.                         methodMetaData.AttributeMetaData.Clear();
  66.                         methodMetaData.AttributeMetaData.Add(interfaceAttr);
  67.                         methods.Add(methodMetaData);
  68.                         continue;
  69.                     }
  70.                 }
  71.                 if (methods.Any())
  72.                     list.Add(new AopCodeBuilderMetaData(classMetaData.NameSpace, classMetaData.Name, methods, classMetaData.Constructor, classMetaData.Usings, classMetaData.InterfaceMetaData));
  73.             }
  74.             return list;
  75.         }
复制代码
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
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

温锦文欧普厨电及净水器总代理

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表