go 依赖注入工具( go-wire )使用(二 用户指南)

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
}

猜你喜欢

转载自blog.csdn.net/luslin1711/article/details/106145938