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 如何完成依赖注入

在开发中,我们创建对象的过程可以分为两步:

  1. 定义结构体的构造函数
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
  1. 调用结构体的构造函数进行实例化
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