千家信息网

怎么使用golang编写基于注解的静态代码增强器/生成器

发表于:2025-11-10 作者:千家信息网编辑
千家信息网最后更新 2025年11月10日,这篇文章主要介绍"怎么使用golang编写基于注解的静态代码增强器/生成器",在日常操作中,相信很多人在怎么使用golang编写基于注解的静态代码增强器/生成器问题上存在疑惑,小编查阅了各式资料,整理
千家信息网最后更新 2025年11月10日怎么使用golang编写基于注解的静态代码增强器/生成器

这篇文章主要介绍"怎么使用golang编写基于注解的静态代码增强器/生成器",在日常操作中,相信很多人在怎么使用golang编写基于注解的静态代码增强器/生成器问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"怎么使用golang编写基于注解的静态代码增强器/生成器"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

Spring

Spring的主要特性:1. 控制反转(Inversion of Control, IoC)2. 面向容器3. 面向切面(AspectOriented Programming, AOP)源码gitee地址:https://gitee.com/ioly/learning.gooop原文链接: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

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

package scannerimport (        "errors"        "learning/gooop/spring/autogen/common"        "learning/gooop/spring/autogen/domain"        "regexp"        "strings")type IAnnotationScanner interface {        ScanAnnotations(s *domain.StructInfo)}type tAnnotationScanner intfunc (me *tAnnotationScanner) ScanAnnotations(s *domain.StructInfo) {        me.scanStructAnnotation(s)        me.scanFieldAnnotation(s)        me.scanMethodAnnotation(s)}func (me *tAnnotationScanner) scanStructAnnotation(s *domain.StructInfo) {        for i := s.LineNO - 1; i >= 0; i-- {                if !me.matchAnnotation(s, i) {                        break                }                code := s.CodeFile.RawLines[i]                e, a := me.parseAnnotation(code)                if e != nil {                        panic(e)                }                s.AppendAnnotation(a)        }}func (me *tAnnotationScanner) scanFieldAnnotation(s *domain.StructInfo) {        for _, fld := range s.Fields {                for i := fld.LineNO - 1; i >= 0; i-- {                        if !me.matchAnnotation(s, i) {                                break                        }                        code := s.CodeFile.RawLines[i]                        e, a := me.parseAnnotation(code)                        if e != nil {                                panic(e)                        }                        fld.AppendAnnotation(a)                }        }}func (me *tAnnotationScanner) scanMethodAnnotation(s *domain.StructInfo) {        for _, method := range s.Methods {                for i := method.LineNO - 1; i >= 0; i-- {                        if !me.matchAnnotation(s, i) {                                break                        }                        code := s.CodeFile.RawLines[i]                        e, a := me.parseAnnotation(code)                        if e != nil {                                panic(e)                        }                        method.AppendAnnotation(a)                }        }}func (me *tAnnotationScanner) matchAnnotation(s *domain.StructInfo, lineNO int) bool {        line := s.CodeFile.RawLines[lineNO]        return gAnnotationStartRegexp.MatchString(line)}func (me *tAnnotationScanner) parseAnnotation(line string) (error, *domain.AnnotationInfo) {        ss := gAnnotationStartRegexp.FindStringSubmatch(line)        if len(ss) <= 0 {                return nil, nil        }        a := domain.NewAnnotationInfo()        // name        declare := ss[0]        a.Name = ss[1]        // properties        t := line[len(declare):]        for {                // space*                b1, s1 := common.Tokens.MatchSpaces(t)                if b1 {                        t = t[len(s1):]                }                // key                b2, s2 := common.Tokens.MatchIdentifier(t)                if !b2 {                        break                }                t = t[len(s2):]                // =                b31, s31 := common.Tokens.MatchSpaces(t)                if b31 {                        t = t[len(s31):]                }                b32 := common.Tokens.MatchString(t, "=")                if !b32 {                        return errors.New("expecting ="), nil                } else {                        t = t[1:]                }                b33, s33 := common.Tokens.MatchSpaces(t)                if b33 {                        t = t[len(s33):]                }                // value                b4, s4, i4 := me.parsePropertyValue(t)                if !b4 {                        return errors.New("expecting attribute value"), nil                } else {                        t = t[i4:]                        a.AppendAttribute(s2, s4)                }        }        return nil, a}func (me *tAnnotationScanner) parsePropertyValue(s string) (bool, string, int) {        // quoted string by ""        b2, s2 := common.Tokens.MatchRegexp(s, `^"((\\")|[^"])*"`)        if b2 {                return true, me.removeDoubleQuote(s2), len(s2)        }        // quoted string by ``        b3, s3 := common.Tokens.MatchRegexp(s, "^`[^`]+`")        if b3 {                return true, s3[1 : len(s3)-1], len(s3)        }        // simple string        b4, s4 := common.Tokens.MatchRegexp(s, `^\S+`)        if b4 {                return true, s4, len(s4)        }        return false, "", 0}func (me *tAnnotationScanner) removeDoubleQuote(s string) string {        s = s[1 : len(s)-1]        arrSpecialChars := [][]string{                {`\r`, "\r"},                {`\n`, "\n"},                {`\t`, "\t"},                {`\"`, "\""},                {`\\`, "\\"},                {`\v`, "\v"},        }        for _, it := range arrSpecialChars {                s = strings.ReplaceAll(s, it[0], it[1])        }        return s}var gAnnotationStartRegexp = regexp.MustCompile(`^//\s*@(\w+)\s*`)var DefaultAnnotationScanner = new(tAnnotationScanner)

scanner/IAnnotationScanner_test.go

针对注解信息的单元测试

package scannerimport (        "encoding/json"        "learning/gooop/spring/autogen/domain"        "strings"        "testing")func Test_AnnotationScanner(t *testing.T) {        code := `// @RestController path=/order scope=singletontype StructInfo struct {        LineNO      int        Name        string        CodeFile    *CodeFileInfo        Fields      []*FieldInfo        Methods     []*MethodInfo        Annotations []*AnnotationInfo}func NewStructInfo() *StructInfo {        it := new(StructInfo)        it.Fields = []*FieldInfo{}        it.Methods = []*MethodInfo{}        it.Annotations = []*AnnotationInfo{}        return it}// @GetMapping path=/AppendFieldfunc (me *StructInfo) AppendField(lineNO int, name string, dataType string) error {        fld := NewFieldInfo()        fld.Struct = me        fld.LineNO = lineNO        fld.Name = name        fld.DataType = dataType        me.Fields = append(me.Fields, fld)        return nil}// @GetMapping path="/AppendMethod"func (me *StructInfo) AppendMethod(method *MethodInfo) (error, string) {        me.Methods = append(me.Methods, method)        return nil, ""}// @PostMapping path=/AppendAnnotationfunc (me *StructInfo) AppendAnnotation(ant *AnnotationInfo) (e error, s string) {        me.Annotations = append(me.Annotations, ant)        return nil, ""}`        file := domain.NewCodeFileInfo()        file.CleanLines = strings.Split(code, "\n")        file.RawLines = file.CleanLines        DefaultStructScanner.ScanStruct(file)        for _, it := range file.Structs {                DefaultAnnotationScanner.ScanAnnotations(it)                j, e := json.MarshalIndent(it, "", "  ")                if e != nil {                        t.Fatal(e)                }                t.Log(string(j))        }}

测试输出

API server listening at: [::]:41281=== RUN   Test_AnnotationScanner    IAnnotationScanner_test.go:63: {          "LineNO": 2,          "Name": "StructInfo",          "Fields": [            {              "LineNO": 3,              "Name": "LineNO",              "DataType": "int",              "Annotations": []            },            {              "LineNO": 4,              "Name": "Name",              "DataType": "string",              "Annotations": []            },            {              "LineNO": 5,              "Name": "CodeFile",              "DataType": "*CodeFileInfo",              "Annotations": []            },            {              "LineNO": 6,              "Name": "Fields",              "DataType": "[]*FieldInfo",              "Annotations": []            },            {              "LineNO": 7,              "Name": "Methods",              "DataType": "[]*MethodInfo",              "Annotations": []            },            {              "LineNO": 8,              "Name": "Annotations",              "DataType": "[]*AnnotationInfo",              "Annotations": []            }          ],          "Methods": [            {              "LineNO": 20,              "Name": "AppendField",              "Arguments": [                {                  "Name": "lineNO",                  "DataType": "int"                },                {                  "Name": "name",                  "DataType": "string"                },                {                  "Name": "dataType",                  "DataType": "string"                }              ],              "Annotations": [                {                  "Name": "GetMapping",                  "Attributes": [                    {                      "Key": "path",                      "Value": "/AppendField"                    }                  ]                }              ],              "Returns": [                {                  "Name": "",                  "DataType": "error"                }              ]            },            {              "LineNO": 31,              "Name": "AppendMethod",              "Arguments": [                {                  "Name": "method",                  "DataType": "*MethodInfo"                }              ],              "Annotations": [                {                  "Name": "GetMapping",                  "Attributes": [                    {                      "Key": "path",                      "Value": "/AppendMethod"                    }                  ]                }              ],              "Returns": [                {                  "Name": "",                  "DataType": "error"                },                {                  "Name": "",                  "DataType": "string"                }              ]            },            {              "LineNO": 37,              "Name": "AppendAnnotation",              "Arguments": [                {                  "Name": "ant",                  "DataType": "*AnnotationInfo"                }              ],              "Annotations": [                {                  "Name": "PostMapping",                  "Attributes": [                    {                      "Key": "path",                      "Value": "/AppendAnnotation"                    }                  ]                }              ],              "Returns": [                {                  "Name": "e",                  "DataType": "error"                }              ]            }          ],          "Annotations": [            {              "Name": "RestController",              "Attributes": [                {                  "Key": "path",                  "Value": "/order"                },                {                  "Key": "scope",                  "Value": "singleton"                }              ]            }          ]        }--- PASS: Test_AnnotationScanner (0.01s)PASSDebugger finished with exit code 0

到此,关于"怎么使用golang编写基于注解的静态代码增强器/生成器"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0