纯干货:用golang ast实现aop

422 阅读1分钟

简述

本文全是简单的干货,直接让我们知道如何实现一个AOP功能。在这个示例中,我们定义了一个名为 MyStruct 的结构体,它有两个字段和两个方法。然后,我们使用 Golang 的 AST 包来遍历源代码的 AST,并找到名为 MyStruct 的结构体的方法。

对于每个方法,我们创建一个新的日志语句,并将其插入到方法体的开头。这样,在调用该方法时,就会自动打印出一条日志信息。

最后,我们使用 Golang 的 printer.Fprint 函数将新生成的 AST 节点输出为 Go 代码。

希望这个示例能够帮助您了解如何使用 Golang 的 AST 包来实现 AOP。

package main

import (
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"log"
	"os"
)

func main() {
	src := `package main

type MyStruct struct {
	Field1 int
	Field2 string
}

func (m *MyStruct) Method1() {
	println(m.Field1)
}

func (m *MyStruct) Method2() {
	println(m.Field2)
}
`
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "", src, 0)
	if err != nil {
		log.Fatal(err)
	}

	ast.Inspect(f, func(n ast.Node) bool {
		switch x := n.(type) {
		case *ast.FuncDecl:
			if x.Recv != nil && len(x.Recv.List) == 1 && x.Recv.List[0].Names[0].Name == "m" && x.Recv.List[0].Type.(*ast.StarExpr).X.(*ast.Ident).Name == "MyStruct" {

				logStmt := &ast.ExprStmt{
					X: &ast.CallExpr{
						Fun: &ast.SelectorExpr{
							X:   ast.NewIdent("log"),
							Sel: ast.NewIdent("Println"),
						},
						Args: []ast.Expr{&ast.BasicLit{
							ValuePos: x.Pos(),
							Kind:     token.STRING,
							Value:    "\"Entering " + x.Name.Name + "\"",
						}},
					},
				}

				x.Body.List = append([]ast.Stmt{logStmt}, x.Body.List...)
				return false
			}

		}
		return true
	})

	printer.Fprint(os.Stdout, fset, f)
}


//----------------输出
package main

type MyStruct struct {
	Field1	int
	Field2	string
}

func (m *MyStruct) Method1() {
	log.Println("Entering Method1")
	println(m.Field1)
}

func (m *MyStruct) Method2() {
	log.Println("Entering Method2")
	println(m.Field2)
}