go 依赖注入工具( go-wire )使用(二 用户指南)
wire 有两个核心概念: 提供者与注入者
提供者providers
providers 是一个能够返回值的方法:
package foobarbaz
type Foo struct {
X int
}
func ProvideFoo() Foo {
return Foo{X: 42}
}
type Bar struct {
X int
}
func ProvideBar(foo Foo) Bar {
return Bar{X: -foo.X}
}
type Baz struct {
X int
}
func ProvideBaz(ctx context.Context, bar Bar) (Baz, error) {
if bar.X == 0 {
return Baz{}, errors.New("cannot provide baz when bar is zero")
}
return Baz{X: bar.X}, nil
}
这些都属于提供者。如果经常使用多个提供者,可以将它们放入提供者集合中:
var FooSet = wire.NewSet(ProvideFoo)
var SuperSet = wire.NewSet(FooSet, ProvideBar)
var ProviderSet = wire.NewSet(SuperSet, ProvideBaz)
// or
// var ProviderSet = wire.NewSet(ProvideFoo, ProvideBar, ProvideBaz)
集合可以 自由组合成为新的集合
注射者Injectors
init.go
package foobarbaz
import (
"context"
"github.com/google/wire"
)
func initializeBaz(ctx context.Context) (Baz, error) {
wire.Build(ProviderSet)
return Baz{}, nil
}
func initializeBar(ctx context.Context) (Bar, error) {
wire.Build(ProviderSet)
return Bar{}, nil
}
func initializeFoo(ctx context.Context) (Foo, error) {
wire.Build(ProviderSet)
return Foo{}, nil
}
这时运行wire命令就能看到生成的wire_gen.go文件:
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package foobarbaz
import (
"context"
)
// Injectors from initBaz.go:
func initializeBaz(ctx context.Context) (Baz, error) {
foo := ProvideFoo()
bar := ProvideBar(foo)
baz, err := ProvideBaz(ctx, bar)
if err != nil {
return Baz{}, err
}
return baz, nil
}
func initializeBar(ctx context.Context) (Bar, error) {
foo := ProvideFoo()
bar := ProvideBar(foo)
return bar, nil
}
func initializeFoo(ctx context.Context) (Foo, error) {
foo := ProvideFoo()
return foo, nil
}
进阶功能
1. 绑定接口
在go 程序中,输出经常是接口的实现,而输入可以是接口。可以通过bind 进行绑定
package bind_interface
import "github.com/google/wire"
type Fooer interface {
Foo() string
}
type MyFooer string
func (b *MyFooer) Foo() string {
return string(*b)
}
func provideMyFooer() *MyFooer {
b := new(MyFooer)
*b = "Hello, World!"
return b
}
type Bar string
func provideBar(f Fooer) string {
// f will be a *MyFooer.
return f.Foo()
}
var Set = wire.NewSet(
provideMyFooer,
wire.Bind(new(Fooer), new(*MyFooer)), // 使用new(*MyFooer) 的结果代替 Fooer 接口
provideBar)
wire.go
// +build wireinject
package bind_interface
import "github.com/google/wire"
func initBar() string {
wire.Build(Set)
return ""
}
wire_gen.go
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package bind_interface
// Injectors from wire.go:
func initBar() string {
myFooer := provideMyFooer()
string2 := provideBar(myFooer)
return string2
}
2. 构造体提供者
可以指定创建构造体时注入的参数:
package provideStruct
import "github.com/google/wire"
type Foo int
type Bar int
func ProvideFoo() Foo {
return 0
}
func ProvideBar() Bar {
return 1
}
type FooBar struct {
MyFoo Foo
MyBar Bar
}
var Set = wire.NewSet(
ProvideFoo,
ProvideBar,
wire.Struct(new(FooBar), "MyFoo", "MyBar"))
这里指定了填充MyFoo 与 MyBar 两个字段
wire.go
// +build wireinject
package provideStruct
import "github.com/google/wire"
func initFooBar() *FooBar {
wire.Build(Set)
return nil
}
wire_gen.go
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package provideStruct
// Injectors from wire.go:
func initFooBar() *FooBar {
bar := ProvideBar()
foo := ProvideFoo()
fooBar := &FooBar{
MyBar: bar,
MyFoo: foo,
}
return fooBar
}
当然,可以指定只填充一个字段,也可以使用“*” 指定全填充,也可以通过添加tag指定哪个字段不填充(与“-”配合使用)
type FooBar struct {
mux sync.Mutex `wire:"-"`
MyFoo Foo
MyBar Bar
}
var Set = wire.NewSet(
ProvideFoo,
ProvideBar,
wire.Struct(new(FooBar), "*"))
3. 绑定值
可以提供一个初始化值
package binding_value
import (
"github.com/google/wire"
)
type Foo struct {
X int
}
func injectFoo() Foo {
wire.Build(wire.Value(Foo{X:42}))
return Foo{}
}
wire结果
func injectFoo() Foo {
foo := _wireFooValue
return foo
}
var (
_wireFooValue = Foo{X: 42}
)
4. 使用结构体的字段作为提供者
type Foo struct {
S string
N int
F float64
}
func getS(foo Foo) string {
return foo.S
}
func provideFoo() Foo {
return Foo{S: "hello",N: 1, F: 3.14}
}
func injectedMessage() string {
wire.Build(provideFoo, getS)
return ""
}
wire结果
func injectedMessage() string {
foo := provideFoo()
string2 := getS(foo)
return string2
}
可以使用wire.FieldsOf 代替
func injectedFieldMessage() string {
wire.Build(provideFoo, wire.FieldsOf(new(Foo),"S"))
return ""
}
结果:
func injectedFieldMessage() string {
foo := provideFoo()
string2 := foo.S
return string2
}
5. 清理方法
可以用于数据库,资源关闭等情况
package cleanFunc
import "github.com/google/wire"
type Foo struct {}
func (f *Foo) Close() {}
func NewFoo() (*Foo, func(), error) {
f := new(Foo)
return f, f.Close, nil
}
type Bar struct {
F *Foo
}
func (b *Bar) Close() {}
func NewBar(f *Foo) (*Bar, func(), error) {
b := &Bar{
F: f,
}
return b, b.Close, nil
}
type Bzz struct {
B *Bar
}
func (b *Bzz) Close() {}
func NewBzz(b *Bar) (*Bzz, func(), error) {
bzz := &Bzz{
B: b,
}
return bzz, bzz.Close, nil
}
var producer = wire.NewSet(NewFoo, NewBar, NewBzz)
wire.go
func InitApp() (*Bzz, func(), error) {
panic(wire.Build(producer))
}
wire_gen.go
func InitApp() (*Bzz, func(), error) {
foo, cleanup, err := NewFoo()
if err != nil {
return nil, nil, err
}
bar, cleanup2, err := NewBar(foo)
if err != nil {
cleanup()
return nil, nil, err
}
bzz, cleanup3, err := NewBzz(bar)
if err != nil {
cleanup2()
cleanup()
return nil, nil, err
}
return bzz, func() {
cleanup3()
cleanup2()
cleanup()
}, nil
}