千家信息网

怎么用golang实现基于注解的静态代码增强器/生成器

发表于:2025-02-08 作者:千家信息网编辑
千家信息网最后更新 2025年02月08日,本篇内容主要讲解"怎么用golang实现基于注解的静态代码增强器/生成器",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"怎么用golang实现基于注解的静态
千家信息网最后更新 2025年02月08日怎么用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编写"基于注解的静态代码增强器/生成器"

    • 配置: ComponentScan,Configuration, Bean

    • Bean声明:Component, Service, Controller

    • Bean注入:Autowried

    • AOP注解:Before, After, Around, PointCut

子目标(Day 6)

  • 昨天把思路撸清楚了,今天动手实现各种词法元素的扫描

    • project.go: 扫描整个项目的所有代码文件。module名从go.mod文件里面取

    • packages.go: 递归扫描某个代码目录

    • files.go: 扫描某个go代码文件,并解析import/struct/field/method等元素

    • imports: 扫描指定代码文件的所有import

    • domain/*.go:词法元素模型集,码略

project.go

扫描整个项目的所有代码文件。module名从go.mod文件里面取

package scannerimport (        "errors"        "io/ioutil"        "learning/gooop/spring/autogen/common"        "learning/gooop/spring/autogen/domain"        "os"        "path"        "strings")func ScanProject(name, dir string) (error, *domain.ProjectInfo) {        e, module := parseModFileAndGetModuleName(dir)        if e != nil {                return e, nil        }        files, e := ioutil.ReadDir(dir)        if e != nil {                return e, nil        }        project := domain.NewProjectInfo()        project.Name = name        project.LocalDir = dir        project.Module = module        for _, file := range files {                if !file.IsDir() {                        continue                }                e, pkg := ScanPackage(project, nil, dir+"/"+file.Name())                if e != nil {                        return e, nil                } else {                        project.AppendPackage(pkg)                }        }        return nil, project}func parseModFileAndGetModuleName(dir string) (error, string) {        modfile := path.Join(dir, gModuleFile)        _, e := os.Stat(modfile)        if e != nil {                return gErrorModuleFileNotFound, ""        }        data, e := ioutil.ReadFile(modfile)        if e != nil {                return e, ""        }        text := string(data)        for _, line := range strings.Split(text, "\n") {                line := strings.TrimSpace(line)                if !common.Tokens.MatchString(line, gModulePrefix) {                        continue                }                if ok, s := common.Tokens.MatchRegexp(line, gModulePattern); ok {                        return nil, strings.TrimSpace(s[len(gModulePrefix)+1:])                }        }        return gErrorProjectModuleNotFound, ""}var gModuleFile = "go.mod"var gModulePrefix = "module"var gModulePattern = "^module\\s+\\w+(/\\w+)*"var gErrorModuleFileNotFound = errors.New("module file not found: go.mod")var gErrorProjectModuleNotFound = errors.New("project module not found in go.mod")

packages.go

递归扫描某个代码目录

package scannerimport (        "io/ioutil"        "learning/gooop/spring/autogen/domain"        "path/filepath"        "strings")func ScanPackage(project *domain.ProjectInfo, parent *domain.PackageInfo, dir string) (error, *domain.PackageInfo) {        pkg := domain.NewPackageInfo()        pkg.Project = project        pkg.Parent = parent        pkg.LocalDir = dir        _, f := filepath.Split(dir)        pkg.Name = f        files, e := ioutil.ReadDir(dir)        if e != nil {                return e, nil        }        for _, file := range files {                if file.IsDir() {                        e, p := ScanPackage(project, pkg, dir+"/"+file.Name())                        if e != nil {                                return e, nil                        } else if p != nil {                                pkg.AppendPackage(p)                        }                } else if strings.HasSuffix(file.Name(), ".go") {                        e, f := ScanCodeFile(pkg, dir+"/"+file.Name())                        if e != nil {                                return e, nil                        } else if f != nil {                                pkg.AppendFile(f)                        }                }        }        return nil, pkg}

files.go

读入某个go代码文件,清除注释,然后解析import/struct/field/method等元素

package scannerimport (        "io/ioutil"        "learning/gooop/spring/autogen/common"        "learning/gooop/spring/autogen/domain"        "regexp"        "strings"        "unicode")func ScanCodeFile(pkg *domain.PackageInfo, file string) (error, *domain.CodeFileInfo) {        fbytes, e := ioutil.ReadFile(file)        if e != nil {                return e, nil        }        ftext := string(fbytes)        lines := strings.Split(ftext, "\n")        for i, it := range lines {                lines[i] = strings.TrimRightFunc(it, unicode.IsSpace)        }        codeFile := domain.NewCodeFileInfo()        codeFile.Package = pkg        codeFile.RawLines = lines        // clean comments        bInParaComment := false        cleanLines := make([]string, len(lines))        for i, it := range lines {                s := it                if bInParaComment {                        // para comment end?                        i := strings.Index(it, gParaCommentEnd)                        if i >= 0 {                                bInParaComment = false                                s = s[i+1:]                        } else {                                cleanLines[i] = ""                                continue                        }                }                if common.Tokens.MatchString(it, gLineCommentPrefix) {                        cleanLines[i] = ""                        continue                }                s = removeParaCommentInLine(it)                i1 := strings.Index(s, gParaCommentStart)                if i1 >= 0 {                        s = s[:i1]                        bInParaComment = true                }                cleanLines[i] = s        }        // parse imports        ScanImport(codeFile)        // todo: parse struct declares/fields/methods        return nil, nil}func removeParaCommentInLine(s string) string {        arr := gParaCommentInLine.FindAllStringIndex(s, -1)        if len(arr) > 0 {                for i := len(arr) - 1; i >= 0; i-- {                        from := arr[i][0]                        to := arr[i][1]                        s = s[:from] + s[to+1:]                }        }        return s}var gLineCommentPrefix = "^\\s*//"var gParaCommentInLine = regexp.MustCompile("/\\*.*\\*/")var gParaCommentStart = "/*"var gParaCommentEnd = "*/"

imports.go

扫描指定代码文件的所有import

package scannerimport (        "learning/gooop/spring/autogen/domain"        "regexp")func ScanImport(file *domain.CodeFileInfo) {        parseSingleImport(file)        parseMultiImports(file)}func parseSingleImport(file *domain.CodeFileInfo) {        for _, it := range file.CleanLines {                if gSingleImportRegexp.MatchString(it) {                        ss := gSingleImportRegexp.FindAllStringSubmatch(it, -1)[0]                        imp := domain.NewImportInfo()                        imp.File = file                        if len(ss) == 3 {                                imp.Alias = ""                                imp.Package = ss[1]                        } else if len(ss) == 5 {                                imp.Alias = ss[1]                                imp.Package = ss[3]                        }                        file.AppendImport(imp)                }        }}func parseMultiImports(file *domain.CodeFileInfo) {        bInBlock := false        for _, it := range file.CleanLines {                if bInBlock {                        if gMultiImportEnd.MatchString(it) {                                bInBlock = false                                continue                        }                        if gImportPackage.MatchString(it) {                                ss := gImportPackage.FindAllStringSubmatch(it, -1)[0]                                imp := domain.NewImportInfo()                                imp.File = file                                if len(ss) == 3 {                                        imp.Alias = ""                                        imp.Package = ss[1]                                } else if len(ss) == 5 {                                        imp.Alias = ss[2]                                        imp.Package = ss[3]                                }                        }                }                if gMultiImportStart.MatchString(it) {                        bInBlock = true                        continue                }        }}var gSingleImportRegexp = regexp.MustCompile(`\s*import\s+((\w+|\.)\s+)?("\w+(/\w+)*")`)var gMultiImportStart = regexp.MustCompile(`^\s*import\s+\(`)var gMultiImportEnd = regexp.MustCompile(`^\s*\)`)var gImportPackage = regexp.MustCompile(`^\s*((\w+|\.)\s+)?("\w+(/\w+)*")`)

到此,相信大家对"怎么用golang实现基于注解的静态代码增强器/生成器"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0