google/wire 是 Go 语言的编译时依赖注入框架,与 Spring IoC 一样,wire 的目标也是让开发者从对我的项目中大量依赖的创立和治理中解脱进去,但两者在实现形式上有着很大的不同。
Go 中的依赖注入
在 Go 中,咱们通常采取在构造函数中传入依赖的形式创建对象:
func main() {NewUserStore(conf.Load(),db.InitMySQL())
}
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
这在小规模我的项目中成果很好,但当我的项目规模变大时,单个对象的创立往往须要多个依赖,而这些依赖通常还有它本人的依赖,这就导致对象的创立变得繁琐,容易出错。
wire 如何实现依赖注入
在开发中,咱们创建对象的过程能够分为两步:
- 定义构造体的构造函数
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
- 调用构造体的构造函数进行实例化
NewUserStore(conf.Load(),db.InitMySQL())
第一步咱们申明了结构构造体须要的依赖,而 wire 做的,就是 帮咱们“写”好第二步的代码。
依赖申明
为了生成第二步中的代码,咱们首先须要将所有的构造函数 (精确的说是所有须要注入的依赖) 进行申明并传递给 wire.Build
办法:
func setup(ctx context.Context) (sv *server.Server, clean func(), err error) {
wire.Build(
conf.Load,
db.InitMySQL,
userstore.NewUserStore,
)
return nil, nil, nil
}
setup
函数的内容最终会被 wire 用上面的实现替换(接下来会进行阐明):
func setup(ctx context.Context) (sv *server.Server, clean func(), err error) {config := conf.Load()
engine := db.InitMySQL()
userstoreHandler, clean1, err := userstore.NewUserStore(config,engine)
if err != nil {clean()
return nil, nil, err
}
sv := server.New(userstoreHandler)
return sv, func() { clean1() }, nil
}
能够看到下面 wire 为咱们“写”好的代码其实跟咱们本人将会写的代码是一样的,能够很容易的读懂。但随着依赖增多,这样的代码就会减少,设想一下,如果我的项目中有上百个依赖,那么就会有上百行的 New...(...), if err != nil {...}
代码。
应用 go generate 生成依赖注入代码
wire 就是做了这样一件事:帮咱们生成所有须要的对象创立代码,开发时咱们只须要在构造体的构造函数中申明本人须要什么。
wire 在理论我的项目中的应用步骤:
- 通过
wire.Bind
办法进行依赖申明,假如这部分代码放在inject.go
文件中 - 应用 go generate 命令生成代码
go generate
须要通过 //go:generate
正文的形式应用,创立 wire_gen.go
文件并增加该正文:
//go:generate wire
- 在我的项目目录下执行
go generate
命令。
运行该命令时,它将扫描与以后包相干的源代码文件,找出所有蕴含
//go:generate
的非凡正文,提取并执行该非凡正文前面的命令。
至此,你就能够看到 wire_gen.go
里曾经生成好 func setup(ctx context.Context) (sv *server.Server, clean func(), err error)
办法,办法体为依赖注入 (对象创立) 代码。
- 最初一步,在 main 办法中调用
setup
办法(这里假如咱们的我的项目是一个提供 RESTful 接口的 HTTP 服务)
package main
func main() {ctx := context.Background()
srv, cleanup, err := setupGCP(ctx)
if err != nil {log.Fatalf("failed to setup the server, got error: %s", err)
}
defer cleanup()
log.Fatal(srv.ListenAndServe(":8080"))
}
须要留神的是,如果 inject.go
和 wire_gen.go
在同一个 package 下,那此时 IDE 会提醒语法错误,因为两个文件下存在办法签名一样的两个办法。此时能够应用 //+build go build
构建束缚对其中一个文件进行排除。
首先在 inject.go
中退出如下的构建标签:
//+build wireinject
而后在 wire_gen.go
中退出构建束缚使 go build
时排除带 wireinject
标签的文件:
//+build !wireinject
应用 wire.Bind, wire.Value 等办法申明和组织依赖
wire 在 go generate
扫描代码时从 wire.Bind
中提取我的项目依赖关系并为咱们生成依赖注入代码,那咱们要怎么将依赖关系更高效,清晰的“告知”给 wire 呢?
wire 提供了几个函数帮忙咱们组织和申明我的项目中的依赖关系:
wire.Bind
: 将接口和其实现进行绑定wire.Value
: 将值 (实例) 包装为依赖
…
更具体的文档能够在 这里 查看。