缘起

最近阅读 [Spring Boot技术内幕: 架构设计与实现原理] (朱智胜 , 2020.6)
本系列笔记拟采用golang练习之
Talk is cheap, show me the code.

Spring

  1. Spring的主要特性:
  2. 1. 控制反转(Inversion of Control, IoC
  3. 2. 面向容器
  4. 3. 面向切面(AspectOriented Programming, AOP
  5. 源码gitee地址:
  6. https://gitee.com/ioly/learning.gooop
  7. 原文链接:
  8. https://my.oschina.net/ioly

目标

  • 参考spring boot常用注解,使用golang编写“基于注解的静态代码增强器/生成器”

子目标(Day 9)

  • struct解析清楚了,接着解析注解就比较容易了
    • scanner/IStructScanner.go:修复scanMethod()和scanAnnotation()的细节问题
    • scanner/IAnnotationScanner.go:注解扫描接口及默认实现。注解的属性支持双引号和重音号字符串。
    • scanner/IAnnotationScanner_test.go:针对注解信息的单元测试

scanner/IAnnotationScanner.go

注解扫描接口及默认实现。注解的属性支持双引号和重音号字符串。

  1. package scanner
  2. import (
  3. "errors"
  4. "learning/gooop/spring/autogen/common"
  5. "learning/gooop/spring/autogen/domain"
  6. "regexp"
  7. "strings"
  8. )
  9. type IAnnotationScanner interface {
  10. ScanAnnotations(s *domain.StructInfo)
  11. }
  12. type tAnnotationScanner int
  13. func (me *tAnnotationScanner) ScanAnnotations(s *domain.StructInfo) {
  14. me.scanStructAnnotation(s)
  15. me.scanFieldAnnotation(s)
  16. me.scanMethodAnnotation(s)
  17. }
  18. func (me *tAnnotationScanner) scanStructAnnotation(s *domain.StructInfo) {
  19. for i := s.LineNO - 1; i >= 0; i-- {
  20. if !me.matchAnnotation(s, i) {
  21. break
  22. }
  23. code := s.CodeFile.RawLines[i]
  24. e, a := me.parseAnnotation(code)
  25. if e != nil {
  26. panic(e)
  27. }
  28. s.AppendAnnotation(a)
  29. }
  30. }
  31. func (me *tAnnotationScanner) scanFieldAnnotation(s *domain.StructInfo) {
  32. for _, fld := range s.Fields {
  33. for i := fld.LineNO - 1; i >= 0; i-- {
  34. if !me.matchAnnotation(s, i) {
  35. break
  36. }
  37. code := s.CodeFile.RawLines[i]
  38. e, a := me.parseAnnotation(code)
  39. if e != nil {
  40. panic(e)
  41. }
  42. fld.AppendAnnotation(a)
  43. }
  44. }
  45. }
  46. func (me *tAnnotationScanner) scanMethodAnnotation(s *domain.StructInfo) {
  47. for _, method := range s.Methods {
  48. for i := method.LineNO - 1; i >= 0; i-- {
  49. if !me.matchAnnotation(s, i) {
  50. break
  51. }
  52. code := s.CodeFile.RawLines[i]
  53. e, a := me.parseAnnotation(code)
  54. if e != nil {
  55. panic(e)
  56. }
  57. method.AppendAnnotation(a)
  58. }
  59. }
  60. }
  61. func (me *tAnnotationScanner) matchAnnotation(s *domain.StructInfo, lineNO int) bool {
  62. line := s.CodeFile.RawLines[lineNO]
  63. return gAnnotationStartRegexp.MatchString(line)
  64. }
  65. func (me *tAnnotationScanner) parseAnnotation(line string) (error, *domain.AnnotationInfo) {
  66. ss := gAnnotationStartRegexp.FindStringSubmatch(line)
  67. if len(ss) <= 0 {
  68. return nil, nil
  69. }
  70. a := domain.NewAnnotationInfo()
  71. // name
  72. declare := ss[0]
  73. a.Name = ss[1]
  74. // properties
  75. t := line[len(declare):]
  76. for {
  77. // space*
  78. b1, s1 := common.Tokens.MatchSpaces(t)
  79. if b1 {
  80. t = t[len(s1):]
  81. }
  82. // key
  83. b2, s2 := common.Tokens.MatchIdentifier(t)
  84. if !b2 {
  85. break
  86. }
  87. t = t[len(s2):]
  88. // =
  89. b31, s31 := common.Tokens.MatchSpaces(t)
  90. if b31 {
  91. t = t[len(s31):]
  92. }
  93. b32 := common.Tokens.MatchString(t, "=")
  94. if !b32 {
  95. return errors.New("expecting ="), nil
  96. } else {
  97. t = t[1:]
  98. }
  99. b33, s33 := common.Tokens.MatchSpaces(t)
  100. if b33 {
  101. t = t[len(s33):]
  102. }
  103. // value
  104. b4, s4, i4 := me.parsePropertyValue(t)
  105. if !b4 {
  106. return errors.New("expecting attribute value"), nil
  107. } else {
  108. t = t[i4:]
  109. a.AppendAttribute(s2, s4)
  110. }
  111. }
  112. return nil, a
  113. }
  114. func (me *tAnnotationScanner) parsePropertyValue(s string) (bool, string, int) {
  115. // quoted string by ""
  116. b2, s2 := common.Tokens.MatchRegexp(s, `^"((\\")|[^"])*"`)
  117. if b2 {
  118. return true, me.removeDoubleQuote(s2), len(s2)
  119. }
  120. // quoted string by ``
  121. b3, s3 := common.Tokens.MatchRegexp(s, "^`[^`]+`")
  122. if b3 {
  123. return true, s3[1 : len(s3)-1], len(s3)
  124. }
  125. // simple string
  126. b4, s4 := common.Tokens.MatchRegexp(s, `^\S+`)
  127. if b4 {
  128. return true, s4, len(s4)
  129. }
  130. return false, "", 0
  131. }
  132. func (me *tAnnotationScanner) removeDoubleQuote(s string) string {
  133. s = s[1 : len(s)-1]
  134. arrSpecialChars := [][]string{
  135. {`\r`, "\r"},
  136. {`\n`, "\n"},
  137. {`\t`, "\t"},
  138. {`\"`, "\""},
  139. {`\\`, "\\"},
  140. {`\v`, "\v"},
  141. }
  142. for _, it := range arrSpecialChars {
  143. s = strings.ReplaceAll(s, it[0], it[1])
  144. }
  145. return s
  146. }
  147. var gAnnotationStartRegexp = regexp.MustCompile(`^//\s*@(\w+)\s*`)
  148. var DefaultAnnotationScanner = new(tAnnotationScanner)

scanner/IAnnotationScanner_test.go

针对注解信息的单元测试

  1. package scanner
  2. import (
  3. "encoding/json"
  4. "learning/gooop/spring/autogen/domain"
  5. "strings"
  6. "testing"
  7. )
  8. func Test_AnnotationScanner(t *testing.T) {
  9. code := `
  10. // @RestController path=/order scope=singleton
  11. type StructInfo struct {
  12. LineNO int
  13. Name string
  14. CodeFile *CodeFileInfo
  15. Fields []*FieldInfo
  16. Methods []*MethodInfo
  17. Annotations []*AnnotationInfo
  18. }
  19. func NewStructInfo() *StructInfo {
  20. it := new(StructInfo)
  21. it.Fields = []*FieldInfo{}
  22. it.Methods = []*MethodInfo{}
  23. it.Annotations = []*AnnotationInfo{}
  24. return it
  25. }
  26. // @GetMapping path=/AppendField
  27. func (me *StructInfo) AppendField(lineNO int, name string, dataType string) error {
  28. fld := NewFieldInfo()
  29. fld.Struct = me
  30. fld.LineNO = lineNO
  31. fld.Name = name
  32. fld.DataType = dataType
  33. me.Fields = append(me.Fields, fld)
  34. return nil
  35. }
  36. // @GetMapping path="/AppendMethod"
  37. func (me *StructInfo) AppendMethod(method *MethodInfo) (error, string) {
  38. me.Methods = append(me.Methods, method)
  39. return nil, ""
  40. }
  41. // @PostMapping path=/AppendAnnotation
  42. func (me *StructInfo) AppendAnnotation(ant *AnnotationInfo) (e error, s string) {
  43. me.Annotations = append(me.Annotations, ant)
  44. return nil, ""
  45. }`
  46. file := domain.NewCodeFileInfo()
  47. file.CleanLines = strings.Split(code, "\n")
  48. file.RawLines = file.CleanLines
  49. DefaultStructScanner.ScanStruct(file)
  50. for _, it := range file.Structs {
  51. DefaultAnnotationScanner.ScanAnnotations(it)
  52. j, e := json.MarshalIndent(it, "", " ")
  53. if e != nil {
  54. t.Fatal(e)
  55. }
  56. t.Log(string(j))
  57. }
  58. }

测试输出

  1. API server listening at: [::]:41281
  2. === RUN Test_AnnotationScanner
  3. IAnnotationScanner_test.go:63: {
  4. "LineNO": 2,
  5. "Name": "StructInfo",
  6. "Fields": [
  7. {
  8. "LineNO": 3,
  9. "Name": "LineNO",
  10. "DataType": "int",
  11. "Annotations": []
  12. },
  13. {
  14. "LineNO": 4,
  15. "Name": "Name",
  16. "DataType": "string",
  17. "Annotations": []
  18. },
  19. {
  20. "LineNO": 5,
  21. "Name": "CodeFile",
  22. "DataType": "*CodeFileInfo",
  23. "Annotations": []
  24. },
  25. {
  26. "LineNO": 6,
  27. "Name": "Fields",
  28. "DataType": "[]*FieldInfo",
  29. "Annotations": []
  30. },
  31. {
  32. "LineNO": 7,
  33. "Name": "Methods",
  34. "DataType": "[]*MethodInfo",
  35. "Annotations": []
  36. },
  37. {
  38. "LineNO": 8,
  39. "Name": "Annotations",
  40. "DataType": "[]*AnnotationInfo",
  41. "Annotations": []
  42. }
  43. ],
  44. "Methods": [
  45. {
  46. "LineNO": 20,
  47. "Name": "AppendField",
  48. "Arguments": [
  49. {
  50. "Name": "lineNO",
  51. "DataType": "int"
  52. },
  53. {
  54. "Name": "name",
  55. "DataType": "string"
  56. },
  57. {
  58. "Name": "dataType",
  59. "DataType": "string"
  60. }
  61. ],
  62. "Annotations": [
  63. {
  64. "Name": "GetMapping",
  65. "Attributes": [
  66. {
  67. "Key": "path",
  68. "Value": "/AppendField"
  69. }
  70. ]
  71. }
  72. ],
  73. "Returns": [
  74. {
  75. "Name": "",
  76. "DataType": "error"
  77. }
  78. ]
  79. },
  80. {
  81. "LineNO": 31,
  82. "Name": "AppendMethod",
  83. "Arguments": [
  84. {
  85. "Name": "method",
  86. "DataType": "*MethodInfo"
  87. }
  88. ],
  89. "Annotations": [
  90. {
  91. "Name": "GetMapping",
  92. "Attributes": [
  93. {
  94. "Key": "path",
  95. "Value": "/AppendMethod"
  96. }
  97. ]
  98. }
  99. ],
  100. "Returns": [
  101. {
  102. "Name": "",
  103. "DataType": "error"
  104. },
  105. {
  106. "Name": "",
  107. "DataType": "string"
  108. }
  109. ]
  110. },
  111. {
  112. "LineNO": 37,
  113. "Name": "AppendAnnotation",
  114. "Arguments": [
  115. {
  116. "Name": "ant",
  117. "DataType": "*AnnotationInfo"
  118. }
  119. ],
  120. "Annotations": [
  121. {
  122. "Name": "PostMapping",
  123. "Attributes": [
  124. {
  125. "Key": "path",
  126. "Value": "/AppendAnnotation"
  127. }
  128. ]
  129. }
  130. ],
  131. "Returns": [
  132. {
  133. "Name": "e",
  134. "DataType": "error"
  135. }
  136. ]
  137. }
  138. ],
  139. "Annotations": [
  140. {
  141. "Name": "RestController",
  142. "Attributes": [
  143. {
  144. "Key": "path",
  145. "Value": "/order"
  146. },
  147. {
  148. "Key": "scope",
  149. "Value": "singleton"
  150. }
  151. ]
  152. }
  153. ]
  154. }
  155. --- PASS: Test_AnnotationScanner (0.01s)
  156. PASS
  157. Debugger finished with exit code 0

(未完待续)