溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么用golang實(shí)現(xiàn)基于注解的靜態(tài)代碼增強(qiáng)器/生成器

發(fā)布時(shí)間:2021-10-13 15:40:30 來源:億速云 閱讀:349 作者:iii 欄目:編程語言

本篇內(nèi)容主要講解“怎么用golang實(shí)現(xiàn)基于注解的靜態(tài)代碼增強(qiáng)器/生成器”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“怎么用golang實(shí)現(xiàn)基于注解的靜態(tài)代碼增強(qiáng)器/生成器”吧!

Spring

Spring的主要特性:
1. 控制反轉(zhuǎn)(Inversion of Control, IoC)
2. 面向容器
3. 面向切面(AspectOriented Programming, AOP)

源碼gitee地址:
https://gitee.com/ioly/learning.gooop

原文鏈接:
https://my.oschina.net/ioly

目標(biāo)

  • 參考spring boot常用注解,使用golang編寫“基于注解的靜態(tài)代碼增強(qiáng)器/生成器”

    • 配置: ComponentScan,Configuration, Bean

    • Bean聲明:Component, Service, Controller

    • Bean注入:Autowried

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

子目標(biāo)(Day 6)

  • 昨天把思路擼清楚了,今天動(dòng)手實(shí)現(xiàn)各種詞法元素的掃描

    • project.go: 掃描整個(gè)項(xiàng)目的所有代碼文件。module名從go.mod文件里面取

    • packages.go: 遞歸掃描某個(gè)代碼目錄

    • files.go: 掃描某個(gè)go代碼文件,并解析import/struct/field/method等元素

    • imports: 掃描指定代碼文件的所有import

    • domain/*.go:詞法元素模型集,碼略

project.go

掃描整個(gè)項(xiàng)目的所有代碼文件。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

遞歸掃描某個(gè)代碼目錄

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

讀入某個(gè)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+)*")`)

到此,相信大家對(duì)“怎么用golang實(shí)現(xiàn)基于注解的靜態(tài)代碼增強(qiáng)器/生成器”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI