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
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
}
New...(...), if err != nil {...}
使用 go generate 生成依赖注入代码
wire 就是做了这样一件事:帮我们生成所有需要的对象创建代码,开发时我们只需要在结构体的构造函数中声明自己需要什么。
wire 在实际项目中的使用步骤:
wire.Bindinject.gogo generate//go:generatewire_gen.go
//go:generate wire
go generate
//go:generate
wire_gen.gofunc setup(ctx context.Context) (sv *server.Server, clean func(), err error)
setup
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.gowire_gen.gogo build
inject.go
//+build wireinject
wire_gen.gogo buildwireinject
//+build !wireinject
使用 wire.Bind, wire.Value 等方法声明和组织依赖
go generatewire.Bind
wire 提供了几个函数帮助我们组织和声明项目中的依赖关系:
wire.Bindwire.Value
更详细的文档可以在 这里 查看。
如何把 wire 应用到实际项目中
go get github.com/google/wire/cmd/wire
下面是一个在实际项目中用 wire 组织代码,实现依赖注入的完整示例:
DuanJiaNing/thewaytowire