golang依赖注入 wire

318 阅读2分钟

快速使用:

1.先安装工具

$ go get github.com/google/wire/cmd/wire

上述命令生成一个可执行程序wire,这就是代码生成器

2.简单使用

main.go

func NewMessage(msg string) Message {
	return Message(msg)
}

type Message string

func NewGreeter(m Message) Greeter {
	return Greeter{Message: m}
}

type Greeter struct {
	Message Message
}

func (g Greeter) Greet() Message {
	return g.Message
}

func NewEvent(g Greeter) Event {
	return Event{Greeter: g}
}

type Event struct {
	Greeter Greeter
}

func (e Event) Start() {
	msg := e.Greeter.Greet()
	fmt.Println(msg)
}

func main() {
	event := InitializeEvent("hello_world")
	event.Start()
}

wire.go

// +build wireinject
// The build tag makes sure the stub is not built in the final build.

package main

import (
	"github.com/google/wire"
)

// InitializeEvent 声明injector的函数签名
func InitializeEvent(msg string) Event {
	wire.Build(NewEvent, NewGreeter, NewMessage)
	return Event{} //返回值没有实际意义,只需符合函数签名即可
}

// +build wireinject wire工具只会处理有wireinject的文件

执行命令 $wire 生成wire_gen.go文件

wire_gen.go

//+build !wireinject

package main

// Injectors from wire.go:

// InitializeEvent 声明injector的函数签名
func InitializeEvent(msg string) Event {
	message := NewMessage(msg)
	greeter := NewGreeter(message)
	event := NewEvent(greeter)
	return event
}

一些概念

provider(构造器)和injector(注入器)是wire的两个核心概念

通过提供provider函数,让wire知道如何产生这些依赖对象。wire根据我们定义的injector函数签名,生成完整的injector函数,injector函数是最终我们需要的函数,它将按依赖顺序调用provider。

provider就是普通的Go函数,可以把它看作是某对象的构造函数,我们通过provider告诉wire该对象的依赖情况

injector是wire生成的函数

高级特性

1.接口绑定

依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。核心思想是面向接口编程。

main.go

packahe main

// User
type User struct {
}

// UserService
type UserService struct {
	userRepo UserRepository // <-- UserService依赖UserRepository接口
}

// UserRepository 存放User对象的数据仓库接口,比如可以是mysql,restful api ....
type UserRepository interface {
	// GetUserByID 根据ID获取User, 如果找不到User返回对应错误信息
	GetUserByID(id int) (*User, error)
}

// NewUserService *UserService构造函数
func NewUserService(userRepo UserRepository) *UserService {
	return &UserService{
		userRepo: userRepo,
	}
}

// mockUserRepo 模拟一个UserRepository实现
type mockUserRepo struct {
	foo string
	bar int
}

// GetUserByID UserRepository接口实现
func (u *mockUserRepo) GetUserByID(id int) (*User, error) {
	return &User{}, nil
}

// NewMockUserRepo *mockUserRepo构造函数
func NewMockUserRepo(foo string, bar int) *mockUserRepo {
	return &mockUserRepo{
		foo: foo,
		bar: bar,
	}
}

func main() {
	userService := InitializeUserService("foo", 100)
	_ = userService
}

wire.go

// +build wireinject
// The build tag makes sure the stub is not built in the final build.

package main

// MockUserRepoSet 将 *mockUserRepo与UserRepository绑定
var MockUserRepoSet = wire.NewSet(NewMockUserRepo, wire.Bind(new(UserRepository), new(*mockUserRepo)))

func InitializeUserService(foo string, bar int) *UserService {
	wire.Build(NewUserService, MockUserRepoSet) // 使用MockUserRepoSet
	return nil
}

不进行绑定时,执行命令$ wire

wire: src\zpool\wire.go:24:1: inject InitializeUserService: no provider found for zpool.UserRepository needed by *zpool.UserService in provider "NewUserService" (src\zpool\main.go:84:6) wire: zpool: generate failed wire: at least one generate failure

wire_gen.go

func InitializeUserService(foo string, bar int) *UserService {
	mainMockUserRepo := NewMockUserRepo(foo, bar)
	userService := NewUserService(mainMockUserRepo)
	return userService
}

2.结构体provider

除了函数外,结构体也可以充当provider的角色

main.go

type Foo int
type Bar int

func ProvideFoo() Foo {
	return 1
}
func ProvideBar() Bar {
	return 2
}

type FooBar struct {
	MyFoo Foo
	MyBar Bar
}

wire.go

// +build wireinject

package main
var Set = wire.NewSet(
	ProvideFoo,
	ProvideBar,
	wire.Struct(new(FooBar), "MyFoo", "MyBar"))

func InitializeFooBar() FooBar {
	wire.Build(Set)
	return FooBar{}
}

wire_gen.go

func InitializeFooBar() FooBar {
	foo := ProvideFoo()
	bar := ProvideBar()
	fooBar := FooBar{
		MyFoo: foo,
		MyBar: bar,
	}
	return fooBar
}

参考原文:zhuanlan.zhihu.com/p/338926709