本篇内容介绍了“编写golang编写注解的静态代码增强器/生成器的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
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编写“基于注解的静态代码增强器/生成器”
因为struct/field/method的扫描是关键,因此今天针对这块做了单元测试
common/Tokens.go:修复MatchBasicType方法的正则匹配bug。其实func类型的DataType也没考虑到,但现在暂时可以用type alias规避,先不追求完美吧。
scanner/IStructScanner.go: 修复若干细节, 并添加返回类型的扫描
scanner/IStructScanner_test.go:struct扫描器的单元测试
修复MatchBasicType方法的正则匹配bug。其实func类型的DataType也没考虑到,但现在暂时可以用type alias规避,先不追求完美吧。
func (me *tTokens) MatchBasicType(s string) (bool, string) { list := []string{ "int", "string", "bool", "byte", "int32", "int64", "uint32", "uint64", "float32", "float64", "int8", "uint8", "int16", "uint16", `time\.Time`, } for _, it := range list { if ok, t := me.MatchRegexp(s, "^"+it+`(\s+|$)`); ok { return true, strings.TrimSpace(t) } } return false, "" }
修复若干细节, 并添加返回类型的扫描
package scanner import ( "errors" "learning/gooop/spring/autogen/common" "learning/gooop/spring/autogen/domain" "regexp" "strings" ) type IStructScanner interface { ScanStruct(file *domain.CodeFileInfo) } type tStructScanner int func (me *tStructScanner) ScanStruct(file *domain.CodeFileInfo) { bInStruct := false vStructs := []*domain.StructInfo{nil} for lineNO, line := range file.CleanLines { if bInStruct { // end? if gStructEndRegexp.MatchString(line) { me.scanMethod(vStructs[0], lineNO+1) file.AppendStruct(vStructs[0]) bInStruct = false vStructs[0] = nil continue } // in struct block ok, fname, ftype := me.scanField(line) if ok { vStructs[0].AppendField(lineNO, fname, ftype) } } else { // not in struck block // matching start? if gStructStartRegexp.MatchString(line) { bInStruct = true ss := gStructStartRegexp.FindStringSubmatch(line) vStructs[0] = domain.NewStructInfo() vStructs[0].LineNO = lineNO vStructs[0].CodeFile = file vStructs[0].Name = ss[1] continue } } } } func (me *tStructScanner) scanField(line string) (ok bool, fldName string, fldType string) { if !gFieldStartRegexp.MatchString(line) { return false, "", "" } t := line s1 := gFieldStartRegexp.FindString(t) fldName = strings.TrimSpace(s1) t = t[len(s1):] b2, s2 := common.Tokens.MatchDataType(t) if !b2 { return false, "", "" } fldType = strings.TrimSpace(s2) return true, fldName, fldType } func (me *tStructScanner) scanMethod(stru *domain.StructInfo, fromLineNO int) { for i, limit := fromLineNO, len(stru.CodeFile.CleanLines); i < limit; i++ { line := stru.CodeFile.CleanLines[i] if !gMethodStartRegex.MatchString(line) { continue } ss := gMethodStartRegex.FindStringSubmatch(line) // declare declare := ss[0] offset := len(declare) // receiver receiver := ss[1] if receiver != stru.Name { continue } method := domain.NewMethodInfo() // name method.Name = ss[2] // method input args e, args := me.scanMethodArgs(method, strings.TrimSpace(line[offset:])) if e != nil { panic(e) } offset += len(args) // method return args e = me.scanReturnArgs(method, strings.TrimSpace(line[offset:])) if e != nil { panic(e) } // end scan method stru.AppendMethod(method) } } func (me *tStructScanner) scanMethodArgs(method *domain.MethodInfo, s string) (error, string) { t := s offset := 0 for { // name b1, s1 := common.Tokens.MatchRegexp(t, `^\w+(\s*,\s*\w+)?\s+`) if !b1 { break } argNames := strings.TrimSpace(s1) offset += len(s1) t = s[offset:] // data type b2, s2 := common.Tokens.MatchDataType(t) if !b2 { return gInvalidMethodArgs, "" } argDataType := s2 offset += len(s2) t = s[offset:] for _, it := range strings.Split(argNames, ",") { method.AppendArgument(it, argDataType) } // ,\s+ b3, s3 := common.Tokens.MatchRegexp(t, `\s*,\s*`) if !b3 { break } offset += len(s3) t = s[offset:] } b4, s4 := common.Tokens.MatchRegexp(t, `^\s*\)`) if !b4 { return errors.New("expecting right bracket"), "" } offset += len(s4) return nil, s[0:offset] } func (me *tStructScanner) scanReturnArgs(method *domain.MethodInfo, s string) error { // no args? if gMethodEndRegexp.MatchString(s) { return nil } // args start t := s b1, s1 := common.Tokens.MatchRegexp(t, `\s*\(\s*`) if !b1 { return errors.New("expecting left bracket") } t = t[len(s1):] // unnamed args? b2, s2 := common.Tokens.MatchDataType(t) if b2 { t = t[len(s2):] method.AppendUnnamedReturn(s2) // more unnamed args? for { b3, s3 := common.Tokens.MatchRegexp(t, `^\s*,\s*`) if !b3 { break } t = t[len(s3):] b4, s4 := common.Tokens.MatchDataType(t) if !b4 { return errors.New("expecting data type") } t = t[len(s4):] method.AppendUnnamedReturn(s4) } } else { // named args? for { // name b3, s3 := common.Tokens.MatchIdentifier(t) if !b3 { return errors.New("expecting identifier") } t = t[len(s3):] // \s+ b4, s4 := common.Tokens.MatchSpaces(t) if !b4 { return errors.New("expecting spaces") } t = t[len(s4):] // type b5, s5 := common.Tokens.MatchDataType(t) if !b5 { return errors.New("expecting data type") } t = t[len(s5):] // more? b6, s6 := common.Tokens.MatchRegexp(t, `^\s*,\s*`) if b6 { // yes more t = t[len(s6):] } else { // no more break } } } // arguments end b7, _ := common.Tokens.MatchRegexp(t, `^\s*\)\s*`) if !b7 { return errors.New("expecting end of arguments") } return nil } var gStructStartRegexp = regexp.MustCompile(`^\s*type\s+(\w+)\s+struct\s+\{`) var gStructEndRegexp = regexp.MustCompile(`^\s*}`) var gFieldStartRegexp = regexp.MustCompile(`^\s*\w+\s+`) var gMethodStartRegex = regexp.MustCompile(`^\s*func\s+\(\s*\w+\s+\*?(\w+)\s*\)\s+(\w+)\s*\(`) var gInvalidMethodArgs = errors.New("invalid method arguments") var gMethodEndRegexp = regexp.MustCompile(`^\s*\{`) var DefaultStructScanner IStructScanner = new(tStructScanner)
struct扫描器的单元测试
package scanner import ( "encoding/json" "learning/gooop/spring/autogen/domain" "strings" "testing" ) func Test_StructScan(t *testing.T) { s := `type _mystruct struct {` t.Log(gStructStartRegexp.MatchString(s)) code := ` 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 } func (me *StructInfo) AppendField(lineNO int, name string, dataType string) { fld := NewFieldInfo() fld.Struct = me fld.LineNO = lineNO fld.Name = name fld.DataType = dataType me.Fields = append(me.Fields, fld) } func (me *StructInfo) AppendMethod(method *MethodInfo) { me.Methods = append(me.Methods, method) } func (me *StructInfo) AppendAnnotation(ant *AnnotationInfo) { me.Annotations = append(me.Annotations, ant) }` file := domain.NewCodeFileInfo() file.CleanLines = strings.Split(code, "\n") DefaultStructScanner.ScanStruct(file) file.CleanLines = nil j, e := json.MarshalIndent(file.Structs, "", " ") if e != nil { panic(e) } t.Log(string(j)) }
API server listening at: [::]:36077 === RUN Test_StructScan IStructScanner_test.go:12: true IStructScanner_test.go:58: [ { "LineNO": 1, "Name": "StructInfo", "Fields": [ { "LineNO": 2, "Name": "LineNO", "DataType": "int", "Annotations": [] }, { "LineNO": 3, "Name": "Name", "DataType": "string", "Annotations": [] }, { "LineNO": 4, "Name": "CodeFile", "DataType": "*CodeFileInfo", "Annotations": [] }, { "LineNO": 5, "Name": "Fields", "DataType": "[]*FieldInfo", "Annotations": [] }, { "LineNO": 6, "Name": "Methods", "DataType": "[]*MethodInfo", "Annotations": [] }, { "LineNO": 7, "Name": "Annotations", "DataType": "[]*AnnotationInfo", "Annotations": [] } ], "Methods": [ { "LineNO": 0, "Name": "AppendField", "Arguments": [ { "Name": "lineNO", "DataType": "int" }, { "Name": "name", "DataType": "string" }, { "Name": "dataType", "DataType": "string" } ], "Annotations": [], "Returns": [] }, { "LineNO": 0, "Name": "AppendMethod", "Arguments": [ { "Name": "method", "DataType": "*MethodInfo" } ], "Annotations": [], "Returns": [] }, { "LineNO": 0, "Name": "AppendAnnotation", "Arguments": [ { "Name": "ant", "DataType": "*AnnotationInfo" } ], "Annotations": [], "Returns": [] } ], "Annotations": [] } ] --- PASS: Test_StructScan (0.01s) PASS Debugger finished with exit code 0
“编写golang编写注解的静态代码增强器/生成器的方法”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。