让 Go 也能注解使用装饰器,切面(AOP)、代理(Proxy)任意函数和方法的神器!

dengsgo · · 816 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

> `go-decorator`, **可以让 Go 通过注释注解使用装饰器的工具,通过装饰器能够切面(AOP)、代理(Proxy)任意的函数和方法,提供观察和控制函数的能力。** `go-decorator` 在八月份发布了初版,经过几个月的多轮迭代,代表着里程碑的版本 v0.20.0 发布了。该版本已经完成了最初开发此工具时设立的目标,即实现比如自由定义函数为装饰器、允许使用装饰器参数、方法集 `type` 快捷注释等等需求,这些最常用的功能现都集成在了这个版本中,同时,保持开发者在使用工具和编写代码时的简单和便捷,减少开发中的心智负担。 目前版本处于公开测试阶段,欢迎 Golang 的开发者安装体验,如有任何问题,可以留言评论或者通过 [GitHub Issue](https://github.com/dengsgo/go-decorator/issues) 提出建议和意见。 | 说明 | 链接 | | ---------------- | ------------------------------------------------------------------------------------ | | 源代码(欢迎 Star 支持!) | [GitHub](https://github.com/dengsgo/go-decorator) | | 使用文档 | [Guide](https://github.com/dengsgo/go-decorator/blob/master/GUIDE.zh_cn.md) | | 代码示例 | [Usages](https://github.com/dengsgo/go-decorator/blob/master/example/usages/main.go) | | 问题跟踪 | [Issue](https://github.com/dengsgo/go-decorator/issues) | | 开源协议 MIT | [MIT License](https://github.com/dengsgo/go-decorator/blob/master/LICENSE) | ## Feature * 添加注释 `//go:decor F` 即可使用装饰器(`F` 为装饰器函数),快速完成“样板代码注入、非侵入式改变函数行为、控制逻辑流程”等逻辑; * 可以自由定义函数作为装饰器,应用于任意一级函数和方法上(top-level function or method); * 支持使用多个(行) `//go:decor` 装饰器装饰目标函数; * 支持注释 `type T types` 类型声明,decorator 会自动装饰代理以 `T` 或者 `*T` 为接收者的所有方法; * 装饰器支持可选参数,给开发带来更多可能; * 支持编译时 `lint` 验证,保证 Go 编译代码的健壮性; * 提供友好的错误提示,可在编译时发现问题并给出错误原因和错误行号(例如未定义的装饰器或未引用的包等); * 仅在编译时增强目标函数,不会降低编译后程序的性能,亦没有反射操作; 装饰器的使用场景,可以参考其他语言,比如 Python、TypeScript。(非常适合在缓存、鉴权、日志等场景使用,作为辅助手段解放重复编码的困扰)。 > `go-decorator` 是一种编译时代码注入技术。使用它不会影响您项目的源文件,并且不会在项目中生成额外的 `.go` 文件和其他冗余文件。这种注入方法与 `go:generate` 生成方式截然不同。 ## Requirement * go 1.18 及其以上 * go.mod 项目 ## Install 通过 `go install` 安装: ```shell $ go install github.com/dengsgo/go-decorator/cmd/decorator@latest ``` 运行 `decorator`,显示 `decorator` 版本信息即为安装成功。 ```shell $ decorator decorator v0.20.0 beta , https://github.com/dengsgo/go-decorator ``` ## Usage `decorator` 依赖原生 `go` 命令来调用它,使用只需在 `go` 的子命令中加入 `-toolexec decorator` 参数即可。 例如: | 原生命令 | 使用 `decorator` | | ------------------ | -------------------------------------- | | `go build` | `go build -toolexec decorator` | | `go run main.go` | `go run -toolexec decorator main.go` | | `go test -v` | `go test -toolexec decorator -v` | | `go install` | `go install -toolexec decorator` | | `go ... -flags...` | `go ... -toolexec decorator -flags...` | ## Code 在你的项目引入装饰器依赖(必须是 go.mod 项目): ```shell $ go get -u github.com/dengsgo/go-decorator ``` 编写类似代码: ```go package main import ( "github.com/dengsgo/go-decorator/decor" "log" ) func main() { // 正常调用你的函数。 // 由于这是一个声明使用装饰器logging的函数, // decorator 编译链会在编译代码时注入装饰器方法logging的调用。 // 所以使用上面的方式编译后运行,你会得到如下输出: // // 2023/08/13 20:26:30 decorator function logging in [] // 2023/08/13 20:26:30 this is a function: myFunc // 2023/08/13 20:26:30 decorator function logging out [] // // 而不是只有 myFunc 本身的一句输出。 // 也就是说通过装饰器改变了这个方法的行为! myFunc() } // 通过使用 go:decor 注释声明该函数将使用装饰器logging来装饰。 // //go:decor logging func myFunc() { log.Println("this is a function: myFunc") } // 这是一个普通的函数 // 但是它实现了 func(*decor.Context [, ...any]) 类型,因此它还是一个装饰器方法, // 可以在其他函数上使用这个装饰器。 // 在函数中,ctx 是装饰器上下文,可以通过 ctx 获取到目标函数的函数名、出入参 // 以及执行目标函数。 // 如果函数中没有执行 ctx.TargetDo(), 那么意味着目标函数不会执行, // 即使你代码里调用了被装饰的目标函数!这时候,目标函数返回的都是零值。 // 在 ctx.TargetDo() 之前,可以修改 ctx.TargetIn 来改变入参值。 // 在 ctx.TargetDo() 之后,可以修改 ctx.TargetOut 来改变返回值。 // 只能改变出入参的值。不要试图改变他们的类型和数量,这将会引发运行时 panic !!! func logging(ctx *decor.Context) { log.Println("decorator function logging in", ctx.TargetIn) ctx.TargetDo() log.Println("decorator function logging out", ctx.TargetOut) } ``` 带有参数的装饰器用法: ```go package main import ( "github.com/dengsgo/go-decorator/decor" ) func main() { optionalParametersFuncDemo() } // 它也是装饰器,不同于普通装饰器,它允许目标函数额外提供参数 `level` 。 // decorator 提供了 lint 语法给开发者,在编译代码时强制进行校验。比如: // `required` 要求目标函数必须对该字段传值; // `nonzero` 要求目标函数传值不能时空值。 // 如果编译时校验不通过,会编译失败。 // 使用方式如下:(更多用法查看 Guide.md): // //go:decor-lint required: {level} //go:decor-lint nonzero: {level} func levelLogging(ctx *decor.Context, level string) { if level == "debug" { // to do something } ctx.TargetDo() } // 这个方法使用了装饰器 levelLogging,并且额外传递了 `level` 参数值 "debug" 给装饰器。 // //go:decor levelLogging#{level: "debug"} func optionalParametersFuncDemo() { // function code } ``` 给 `type T types` 类型声明添加注释 `//go:decor F`,decorator 会自动使用装饰器 `F` 装饰代理以 `T` 或者 `*T` 为接收者的所有方法: ```go package main import ( "github.com/dengsgo/go-decorator/decor" ) // 添加注释//go:decor dumpTargetType, // structType 的方法集 Name、StrName、empty 会自动被装饰器 dumpTargetType 代理装饰。 // 方法的接收者可以是值接收者,也可以是指针接收者,都会被自动装饰。 //go:decor dumpTargetType type structType struct { name string } func (s *structType) Name() string { return s.name } func (s *structType) StrName(name string) { s.name = name } func (s *structType) empty() {} ``` ## Issue 发现任何问题,都可以在这里反馈. [github issues](https://github.com/dengsgo/go-decorator/issues) 。 欢迎 [Star](https://github.com/dengsgo/go-decorator) 关注项目发展!

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

816 次点击  ∙  2 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传