`go-decorator` 是用于 Go 语言编译器的中间件工具,能够通过注释方式实现非侵入式的装饰器使用。
## Feature
- 使用 `//go:decor decoratorfunctionName` 注释函数即可使用装饰器 `decoratorfunctionName`,快速完成样板代码注入、非侵入式改变函数行为、控制逻辑流程等;
- 定义 `func(*decor.Context)` 类型的函数,即可作为装饰器,应用于任意一级函数(top-level function);
- 支持使用多个(行)`//go:decor`装饰器来装饰函数;
- 提供友好的错误提示,可在编译时发现问题并给出错误原因和错误行号(例如未定义的装饰器或未引用的包等);
- 仅在编译时增强目标函数,不会降低编译后程序的性能,也没有反射操作;
- 提供基础使用指南。
装饰器的使用场景,可以类比其他语言,比如 Python、TypeScript。(非常适合在缓存、鉴权、日志等场景使用,作为辅助手段解放重复编码的困扰)。
`go-decorator` 是一种编译时装饰器注入技术。使用它不会影响您项目的源文件,并且不会在项目中生成额外的 `.go` 文件或其他冗余文件。这种注入方法与 `go:generate` 生成方式截然不同。
GitHub: <https://github.com/dengsgo/go-decorator>
-----------
## 使用样例
创建一个项目,我们这里module名为 `demo`.
在你的项目引入装饰器依赖(go.mod 项目):
```
$ go mod init
$ go get -u github.com/dengsgo/go-decorator
```
![]()![](<> "点击并拖拽以移动")
创建 `main.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) 类型,因此它还是一个装饰器方法,
// 可以在其他函数上使用这个装饰器。
// 在函数中,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 install` 安装 `decorator` 工具:
```
$ go install github.com/dengsgo/go-decorator/cmd/decorator@latest
```
![]()![](<> "点击并拖拽以移动")
运行 `decorator`,显示 `decorator` 版本信息即为安装成功。
```
$ decorator
decorator 0.1.0 beta , https://github.com/dengsgo/go-decorator
```
![]()![](<> "点击并拖拽以移动")
`decorator` 是 `go` 的编译链工具,依靠 `go` 命令来调用它运行,进行代码的编译。
所以我们编译代码需要在 `go build` 命令中加入 `-toolexec decorator` 参数。
编译&&运行:
```
$ go build -toolexec decorator
$ ./demo
```
![]()![](<> "点击并拖拽以移动")
会有类似输出打印:
```
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 []
```
![]()![](<> "点击并拖拽以移动")
说明 `go-decorator` 已经成功加入了编译链,并注入了装饰器。
详细的使用文档可以访问下面的链接:
[Guide](https://github.com/dengsgo/go-decorator/blob/master/GUIDE.zh_cn.md) | [Guide 国内Gitee](https://gitee.com/dengsgo/go-decorator/blob/master/GUIDE.zh_cn.md)
项目代码:
GitHub: <https://github.com/dengsgo/go-decorator>
有一些大家可能会关心的点,这里也提一下:
## 条件和限制
以下几种情况需要注意:
- 使用装饰器的目标函数范围**仅限当前项目内**。依赖的其他库即使使用 `//go:decor`也**无法**被装饰。
例如,你的项目module名称是 `a/b/c` ,那么 `//go:decor` 只在 `a/b/c` 及其子包中生效(`a/b/c/d` 有效,`a/m/`无效)。
但是`//go:decor`可以使用任意包的装饰器,没有范围限制。
- **不能**在同一个目标函数上同时使用相同的装饰器重复装饰;
- **不能**对装饰器函数应用装饰器;
- 升级 `decorator` 后或者调整编译参数可能需要在 go 命令中追加 `-a` 参数**强制编译**一次,以覆盖旧的编译缓存。
## 开发与调试
`decorator` 作为 go 编译链中的一环,编译时被 go 编译器加载使用。它与 go 的编译链保持兼容,不会产生副作用。
开发流程中要改变的只是给用到的 go 命令增加 `-toolexec decorator` 参数,其他完全一致,感觉不到有变化。
你也可以随时取消这个参数。放弃项目对 go 装饰器的使用。即使代码中保留了 `//go:decor` 注释也不会有任何副作用(因为它对于标准工具链来说只是无意义的注释而已)。
调试同理。
例如,在 vscode 中,编辑 `launch.json`:
```
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch file",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${file}",
"buildFlags": "-toolexec decorator"
}
]
}
```
![]()![](<> "点击并拖拽以移动")
添加 `"buildFlags": "-toolexec decorator"` 这一行以启用 `decorator` 的装饰器编译。
然后正常断点调试即可。
> 调试体验会不断完善,如果发现问题请让我知道 [Issues](https://github.com/dengsgo/go-decorator/issues)。
## 性能
尽管 `decorator` 在编译时会对目标函数做额外的处理,但它仅仅只构建必要的上下文参数,没有额外开销,更没有反射。相对于原始go代码直接调用装饰器函数来讲,性能几乎是一致的。
## 最后
项目初期,可能会遇到各种各样的问题,随时欢迎 Issues 反馈给我, [Issues](https://github.com/dengsgo/go-decorator/issues) 。
最后最后,强调一下,**现阶段不要将它用于生产环境!现阶段不要将它用于生产环境!现阶段不要将它用于生产环境!**
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传