3 minutes reading time
今天我们来讲一讲 Wire 的入门使用。
go 的依赖注入工具常见有2种,一类是通过反射在运行时进行依赖注入,典型代表是 uber 开源的 dig,另外一类是通过 generate 进行代码生成,典型代表就是我今天要将的 Google 开源的 wire。使用 dig 功能会强大一些,但是缺点就是错误只能在运行时才能发现,这样如果不小心的话可能会导致一些隐藏的 bug 出现。使用 wire 的缺点就是功能限制多一些,但是好处就是编译的时候就可以发现问题,并且生成的代码其实和我们自己手写相关代码差不太多,更符合直觉,心智负担更小。
Wire 的安装十分简单, 只要执行 go get github.com/google/wire/cmd/wire , wire 命令行工具就会被安装到 $GOPATH/bin 目录下。
在正式使用前,我来介绍一下 Wire 中的2个重要概念: Provider 和 Injector。
Provider 是一个普通的函数,这个函数会返回构建依赖关系所需的组件。如下所示,就是一个 provider 函数,在实际使用的时候,往往是一些简单工厂函数。
func NewDb(opt *DbOpt)(*Db, error)
在使用中,不能存在2个 Provider 返回相同的类型。
Injector 是由 wire 自动生成的函数。函数内部会按根据依赖顺序调用相关 Provider。
我们往往在 wire.go 文件中定义 Injector 函数签名。然后通过 wire 命令行工具生成完整函数。由于 wire.go 中的函数并没有真正返回值,为避免编译器报错, 简单地用 panic 函数包装起来即可。不用担心执行时报错, 因为它不会实际运行,只是用来生成真正的代码的依据。
下面看一个 wire.go 的例子:
func initApp(*conf.Server, *conf.Data, log.Logger) (*kratos.App, func(), error) // +build wireinject
// The build tag makes sure the stub is not built in the final build.
package main
import (
"helloworld/internal/biz"
"helloworld/internal/conf"
"helloworld/internal/data"
"helloworld/internal/server"
"helloworld/internal/service"
"github.com/go-kratos/kratos/v2"
"github.com/go-kratos/kratos/v2/log"
"github.com/google/wire"
)
// initApp init kratos application.
func initApp(*conf.Server, *conf.Data, log.Logger) (*kratos.App, func(), error)
运行 wire . 命令就会生成 wire_gen.go 文件, 会生成 Injector 函数的真正实现, wire_gen.go:
// Code generated by Wire. DO NOT EDIT.
//+build !wireinject
package main
import (
"github.com/go-kratos/kratos/v2"
"github.com/go-kratos/kratos/v2/log"
"helloworld/internal/biz"
"helloworld/internal/conf"
"helloworld/internal/data"
"helloworld/internal/server"
"helloworld/internal/service"
)
// Injectors from wire.go:
// initApp init kratos application.
func initApp(confServer *conf.Server, confData *conf.Data, logger log.Logger) (*kratos.App, func(), error)
然后我们就可以使用生成的函数了,如下:
package main
import (
"flag"
"os"
"helloworld/internal/conf"
"github.com/go-kratos/kratos/v2"
"github.com/go-kratos/kratos/v2/config"
"github.com/go-kratos/kratos/v2/config/file"
"github.com/go-kratos/kratos/v2/log"
"github.com/go-kratos/kratos/v2/transport/grpc"
"github.com/go-kratos/kratos/v2/transport/http"
)
func main()
如果不小心忘记了某个 Provider, wire 会报出具体的错误, 帮忙开发人员迅速定位问题。