温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2021-10-13 15:40:30 来源:亿速云 阅读:354 作者:iii 栏目:编程语言

本篇内容主要讲解“怎么用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 scanner

import (
	"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 scanner

import (
	"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 scanner

import (
	"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 scanner

import (
	"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实现基于注解的静态代码增强器/生成器”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI