怎么使用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 scanner import ( "errors" "learning/gooop/spring/autogen/common" "learning/gooop/spring/autogen/domain" "regexp" "strings" ) type IAnnotationScanner interface { ScanAnnotations(s *domain.StructInfo) } type tAnnotationScanner int func (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 scanner import ( "encoding/json" "learning/gooop/spring/autogen/domain" "strings" "testing" ) func Test_AnnotationScanner(t *testing.T) { code := ` // @RestController path=/order scope=singleton type 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=/AppendField func (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=/AppendAnnotation func (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) PASS Debugger finished with exit code 0
到此,关于“怎么使用golang编写基于注解的静态代码增强器/生成器”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注创新互联网站,小编会继续努力为大家带来更多实用的文章!
网页标题:怎么使用golang编写基于注解的静态代码增强器/生成器
分享地址:http://ybzwz.com/article/podjgp.html