What is wire?
wire是google开源的依赖注入框架。或者引用官方的话来说:“Wire is a code generation tool that automates connecting components using dependency injection.”。
Why wire?
runtime dependency injectioncompile-time dependency injection
Provider & Injector
providerinjectorwire
provider: a function that can produce a value. These functions are ordinary Go code.
injector: a function that calls providers in dependency order. With Wire, you write the injector’s signature, then Wire generates the function’s body.
providerwirewireinjectorinjectorinjectorprovider
provider
providerproviderwire
// NewUserStore是*UserStore的provider,表明*UserStore依赖于*Config和 *mysql.DB.
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
// NewDefaultConfig是*Config的provider,没有依赖
func NewDefaultConfig() *Config {...}
// NewDB是*mysql.DB的provider,依赖于ConnectionInfo
func NewDB(info ConnectionInfo) (*mysql.DB, error) {...}
// UserStoreSet 可选项,可以使用wire.NewSet将通常会一起使用的依赖组合起来。
var UserStoreSet = wire.NewSet(NewUserStore, NewDefaultConfig)
injector
injectorwireinjectorprovider
// File: wire_gen.go
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
// initUserStore是由wire生成的injector
func initUserStore(info ConnectionInfo) (*UserStore, error) {
// *Config的provider函数
defaultConfig := NewDefaultConfig()
// *mysql.DB的provider函数
db, err := NewDB(info)
if err != nil {
return nil, err
}
// *UserStore的provider函数
userStore, err := NewUserStore(defaultConfig, db)
if err != nil {
return nil, err
}
return userStore, nil
}
injectormain.goinitUserStore
wireinjector
injectorwire.Buildinjectorprovider
例如:
// initUserStore用于声明injector的函数签名
func initUserStore(info ConnectionInfo) (*UserStore, error) {
// wire.Build声明要获取一个UserStore需要调用到哪些provider函数
wire.Build(UserStoreSet, NewDB)
return nil, nil // 这些返回值wire并不关心。
}
wireinjectorwireinjector
injectorfunc initUserStore(info ConnectionInfo) (*UserStore, error)*UserStorewire.Build*UserStoreprovider:NewUserStorefunc NewUserStore(cfg *Config, db *mysql.DB)NewUserStore*Config*mysql.DBwire.Build*Config*mysql.DBprovider:NewDefaultConfigNewDBfunc NewDefaultConfig() *Config*Configfunc NewDB(info *ConnectionInfo) (*mysql.DB, error)*mysql.DBConnectionInfowire.BuildConnectionInfoproviderNewDBerrorproviderinjector
最佳实践
Installing
$ go get github.com/google/wire/cmd/wire
Quick Start
wire
$ ls
main.go wire.go
main.go
package main
import "fmt"
type Message struct {
msg string
}
type Greeter struct {
Message Message
}
type Event struct {
Greeter Greeter
}
// NewMessage Message的构造函数
func NewMessage(msg string) Message {
return Message{
msg:msg,
}
}
// NewGreeter Greeter构造函数
func NewGreeter(m Message) Greeter {
return Greeter{Message: m}
}
// NewEvent Event构造函数
func NewEvent(g Greeter) Event {
return Event{Greeter: g}
}
func (e Event) Start() {
msg := e.Greeter.Greet()
fmt.Println(msg)
}
func (g Greeter) Greet() Message {
return g.Message
}
// 使用wire前
func main() {
message := NewMessage("hello world")
greeter := NewGreeter(message)
event := NewEvent(greeter)
event.Start()
}
/*
// 使用wire后
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{} //返回值没有实际意义,只需符合函数签名即可
}
wire
$ wire
wire: github.com/DrmagicE/wire-examples/quickstart: wrote XXXX\github.com\DrmagicE\wire-examples\quickstart\wire_gen.go
$ ls
main.go wire.go wire_gen.go
wire生成的文件: wire_gen.go
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func InitializeEvent(msg string) Event {
message := NewMessage(msg)
greeter := NewGreeter(message)
event := NewEvent(greeter)
return event
}
使用前 V.S 使用后
/*
// 使用wire前
func main() {
message := NewMessage("hello world")
greeter := NewGreeter(message)
event := NewEvent(greeter)
event.Start()
}*/
// 使用wire后
func main() {
event := InitializeEvent("hello_world")
event.Start()
}
示例代码: quickstart
quickstartNewMessageNewGreeterNewEventproviderwire_gen.goInitializeEventinjectorinjectorprovider
wire.goinjector
// +build wireinject
...
injectorwire.Buildprovider
// InitializeEvent 声明injector的函数签名
func InitializeEvent(msg string) Event{
wire.Build(NewEvent, NewGreeter, NewMessage) // <--- 传入provider函数
return Event{} //返回值没有实际意义,只需符合函数签名即可
}
该方法的返回值没有实际意义,只需要符合函数签名的要求即可。
高级特性
接口绑定
根据依赖倒置原则(Dependence Inversion Principle),对象应当依赖于接口,而不是直接依赖于具体实现。
quickstartwire
// 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,
}
}
// MockUserRepoSet 将 *mockUserRepo与UserRepository绑定
var MockUserRepoSet = wire.NewSet(NewMockUserRepo,wire.Bind(new(UserRepository), new(*mockUserRepo)))
UserServiceUserRepositorymockUserRepoUserRepositorymockUserRepoprovider*mockUserRepowirewire.NewSetwire.Bind*mockUserRepoUserRepository
/ MockUserRepoSet 将 *mockUserRepo与UserRepository绑定
var MockUserRepoSet = wire.NewSet(NewMockUserRepo,wire.Bind(new(UserRepository), new(*mockUserRepo)))
定义injector函数签名:
func InitializeUserService(foo string, bar int) *UserService{
wire.Build(NewUserService,MockUserRepoSet) // 使用MockUserRepoSet
return nil
}
组合Provider
providerprovider setquickstart
// NewMessage Message的构造函数
func NewMessage(msg string) Message {
return Message{
msg:msg,
}
}
// NewGreeter Greeter构造函数
func NewGreeter(m Message) Greeter {
return Greeter{Message: m}
}
// NewEvent Event构造函数
func NewEvent(g Greeter) Event {
return Event{Greeter: g}
}
func (e Event) Start() {
msg := e.Greeter.Greet()
fmt.Println(msg)
}
// EventSet Event通常是一起使用的一个集合,使用wire.NewSet进行组合
var EventSet = wire.NewSet(NewEvent, NewMessage, NewGreeter)
Eventwire.NewSetinjector
func InitializeEvent(msg string) Event{
//wire.Build(NewEvent, NewGreeter, NewMessage)
wire.Build(EventSet)
return Event{}
}
EventSetwire.Build
示例代码: provider-set
结构体provider
provider
ype Foo int
type Bar int
func ProvideFoo() Foo {
return 1
}
func ProvideBar() Bar {
return 2
}
type FooBar struct {
MyFoo Foo
MyBar Bar
}
var Set = wire.NewSet(
ProvideFoo,
ProvideBar,
wire.Struct(new(FooBar), "MyFoo", "MyBar"))
wire.Struct
var Set = wire.NewSet(
ProvideFoo,
ProvideBar,
wire.Struct(new(FooBar), "*")) // * 表示注入全部字段
生成的injector函数:
func InitializeFooBar() FooBar {
foo := ProvideFoo()
bar := ProvideBar()
fooBar := FooBar{
MyFoo: foo,
MyBar: bar,
}
return fooBar
}
示例代码: struct-provider
区分类型
injectorwire
type FooBar struct {
foo string
bar string
}
func NewFooBar(foo string, bar string) FooBar {
return FooBar{
foo: foo,
bar: bar,
}
}
injector函数签名定义:
// wire无法得知入参a,b跟FooBar.foo,FooBar.bar的对应关系
func InitializeFooBar(a string, b string) FooBar {
wire.Build(NewFooBar)
return FooBar{}
}
providerinjectorwire
provider has multiple parameters of type string
wireFooBar.fooFooBar.bar
type Foo string
type Bar string
type FooBar struct {
foo Foo
bar Bar
}
func NewFooBar(foo Foo, bar Bar) FooBar {
return FooBar{
foo: foo,
bar: bar,
}
}
injector函数签名定义:
func InitializeFooBar(a Foo, b Bar) FooBar {
wire.Build(NewFooBar)
return FooBar{}
}
provider
type MySQLConnectionString string
type FileReader io.Reader
Options 结构体
provideroptions
type Message string
// Options
type Options struct {
Messages []Message
Writer io.Writer
Reader io.Reader
}
type Greeter struct {
}
// NewGreeter Greeter的provider方法使用Options以避免构造函数过长
func NewGreeter(ctx context.Context, opts *Options) (*Greeter, error) {
return nil, nil
}
// GreeterSet 使用wire.Struct设置Options为provider
var GreeterSet = wire.NewSet(wire.Struct(new(Options), "*"), NewGreeter)
injector
func InitializeGreeter(ctx context.Context, msg []Message, w io.Writer, r io.Reader) (*Greeter, error) {
wire.Build(GreeterSet)
return nil, nil
}
示例代码: options-structs
error返回
providererrorwireprovidererror
// Config 配置
type Config struct {
// RemoteAddr 连接的远程地址
RemoteAddr string
}
// APIClient API客户端
type APIClient struct {
c Config
}
// NewAPIClient APIClient构造函数,如果入参校验失败,返回错误原因
func NewAPIClient(c Config) (*APIClient,error) { // <-- 第二个参数设置成error
if c.RemoteAddr == "" {
return nil, errors.New("没有设置远程地址")
}
return &APIClient{
c:c,
},nil
}
// Service
type Service struct {
client *APIClient
}
// NewService Service构造函数
func NewService(client *APIClient) *Service{
return &Service{
client:client,
}
}
injectorerror
...
func InitializeClient(config Config) (*Service, error) { // <-- 第二个参数设置成error
wire.Build(NewService,NewAPIClient)
return nil,nil
}
...
wireinjector
func InitializeClient(config Config) (*Service, error) {
apiClient, err := NewAPIClient(config)
if err != nil { // <-- 在构造依赖的顺序中如果发生错误,则会返回对应的"零值"和相应错误
return nil, err
}
service := NewService(apiClient)
return service, nil
}
在构造依赖的顺序中如果发生错误,则会返回对应的"零值"和相应错误。
示例代码: return-error
Cleanup 处理
providercleanupprovider
// FileReader
type FileReader struct {
f *os.File
}
// NewFileReader *FileReader 构造函数,第二个参数是cleanup function
func NewFileReader(filePath string) (*FileReader, func(), error){
f, err := os.Open(filePath)
if err != nil {
return nil,nil,err
}
fr := &FileReader{
f:f,
}
fn := func() {
log.Println("cleanup")
fr.f.Close()
}
return fr,fn,nil
}
providerfunc()cleanup functionerror
wireprovider
func()errorfunc()error