手撸golang 仿spring ioc/aop 之6 扫码1

缘起

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

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+)*")`)

(未完待续)