
Go 语言本身不支持 Java 或 Python 风格的运行时注解(annotations),但可通过解析源码 AST 获取函数前的文档注释(如 // @xxx),结合 go/ast 和 go/parser 包实现自定义标签提取。
go 语言本身不支持 java 或 python 风格的运行时注解(annotations),但可通过解析源码 ast 获取函数前的文档注释(如 `// @xxx`),结合 `go/ast` 和 `go/parser` 包实现自定义标签提取。
在 Go 中,// @xxx 这类以 @ 开头的行并非语言原生注解,而是开发者约定的文档标记(doc comment),类似于 godoc 解析的注释格式。Go 标准库不提供反射式获取函数注释的 API(如 func.GetAnnotations()),但可通过 go/ast 包静态解析源文件,提取函数节点前的完整文档注释,并按规则匹配 @ 标签。
以下是一个完整示例,用于从指定 .go 文件中提取 Tags() 函数的所有 @ 标签:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"strings"
)
func extractAnnotations(filename, funcName string) ([]string, error) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
if err != nil {
return nil, err
}
var annotations []string
ast.Inspect(f, func(n ast.Node) bool {
// 查找函数声明
if fd, ok := n.(*ast.FuncDecl); ok && fd.Name.Name == funcName {
if fd.Doc != nil {
// 遍历文档注释每行
for _, comment := range fd.Doc.List {
line := strings.TrimSpace(comment.Text)
if strings.HasPrefix(line, "// @") {
tag := strings.TrimPrefix(strings.TrimSpace(line[3:]), "@")
annotations = append(annotations, strings.TrimSpace(tag))
}
}
}
return false // 找到即停止遍历
}
return true
})
return annotations, nil
}
func main() {
tags, err := extractAnnotations("example.go", "Tags")
if err != nil {
panic(err)
}
fmt.Printf("Found annotations: %v\n", tags) // 输出:["annotation1" "annotation2"]
}
⚠️ 注意事项:
- 此方法依赖源码文件(.go),无法在运行时动态获取(区别于反射式 tag);
- fd.Doc 仅包含紧邻函数上方、且被 // 或 /* */ 包裹的注释块,空行会中断关联;
- 不支持跨包或已编译二进制中的注释提取;
- 生产环境建议封装为构建工具插件(如 go generate + 自定义解析器),避免运行时开销。
总结:Go 哲学倾向于显式、简洁与编译期确定性,因此未内置注解机制。对于 API 文档(Swagger)、路由映射(如 Gin 的 // @Router)、代码生成等场景,社区普遍采用 go/ast 静态分析 + 约定注释格式的方案——既保持语言轻量性,又满足元数据表达需求。











