golang中的依赖注入之wire的使用

 更新时间:2026年02月26日 09:36:02   作者:凉凉的知识库  
wire是Google开源的一个依赖注入工具,它是一个代码生成器,本文就来介绍一下golang中的依赖注入之wire的使用,具有一定的参考价值,感兴趣的可以了解一下

Wire

wire是 Google 开源的一个依赖注入工具。它是一个代码生成器,并不是一个框架。我们只需要在一个特殊的go文件中告诉wire类型之间的依赖关系,它会自动帮我们生成代码,帮助我们创建指定类型的对象,并组装它的依赖。

安装

$ go install github.com/google/wire/cmd/wire@latest

两个概念

正式开始前需要先了解一下 wire 当中的两个概念:providerinjector

Provider

Provider 是一个普通的函数,这个函数会返回构建依赖关系所需的组件。如下所示,就是一个 provider 函数,在实际使用的时候,往往是一些简单的工厂函数,这个函数不会太复杂。

// NewPostRepo 创建文章 Repo
func NewPostRepo() IPostRepo {}

不过需要注意的是在 wire 中不能存在两个 provider 返回相同的组件类型

Injector

injector 也是一个普通函数,我们常常在 wire.go 文件中定义 injector 函数签名,然后通过 wire 命令自动生成一个完整的函数

//+build wireinject

func GetBlogService() *Blog {
    panic(wire.Build(NewBlogService, NewPostUsecase, NewPostRepo))
}

第一行的 //+build wireinject 注释确保了这个文件在我们正常编译的时候不会被引用,而 wire . 生成的文件 wire_gen.go 会包含 //+build !wireinject 注释,正常编译的时候,不指定 tag 的情况下会引用这个文件

wire.Buildinjector 函数中使用,用于表名这个 injector 由哪些 provider 提供依赖, injector 函数本身只是一个函数签名,所以我们直接在函数中 panic 实际生成代码的时候并不会直接调用 panic

示例

type Message string

func NewMessage() Message {
	return Message("Hi there!")
}

type Greeter struct {
	Message Message
}

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

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

type Event struct {
	Greeter Greeter // <- adding a Greeter field
}

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

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

设计一个程序,其中 Event依赖GreeterGreeter依赖Message

如果我们手动构建Event则需要

func main() {
    message := NewMessage()
    greeter := NewGreeter(message)
    event := NewEvent(greeter)

    event.Start()
}

使用wire生成代码

对于上面的NewEventNewGreeterNewMessage都是provider

我们只要再创建一个独立的文件 wrie.go,并在里面创建一个Injector即可

//go:build wireinject
// +build wireinject

package main

import "github.com/google/wire"

func InitializeEvent() Event {
	panic(wire.Build(NewEvent, NewGreeter, NewMessage))
}

在执行完wire命令之后就会生成wire_gen.go,它的内容就是构建 Event。我们需要连同wire_gen.go一起提交到版本控制系统里

// Code generated by Wire. DO NOT EDIT.

//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject

package main

// Injectors from wire.go:

func InitializeEvent() Event {
	message := NewMessage()
	greeter := NewGreeter(message)
	event := NewEvent(greeter)
	return event
}

此时我们就可以直接在main里这样用

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

返回错误

在 go 中如果遇到错误,我们会在最后一个返回值返回 error,wire 同样也支持返回错误的情况,只需要在 injector 的函数签名中加上 error 返回值即可

调整Provider的签名

type Event struct {
	Greeter Greeter
}

//func NewEvent(g Greeter) Event {
//	return Event{Greeter: g}
//}
func NewEvent(g Greeter) (Event, error) {
	if time.Now().Unix()%2 == 0 {
		return Event{}, errors.New("could not create event: event greeter is grumpy")
	}

	return Event{Greeter: g}, nil
}

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

调整Injector的签名

func InitializeEvent() (Event, error) {
	panic(wire.Build(NewEvent, NewGreeter, NewMessage))
}

生成的代码如下所示,可以发现会像我们自己写代码一样判断一下 if err 然后返回

// Code generated by Wire. DO NOT EDIT.

//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject

package main

// Injectors from wire.go:

func InitializeEvent() (Event, error) {
	message := NewMessage()
	greeter := NewGreeter(message)
	event, err := NewEvent(greeter)
	if err != nil {
		return Event{}, err
	}
	return event, nil
}

main中的调用

func main() {
    e, err := InitializeEvent()
    if err != nil {
        fmt.Printf("failed to create event: %s\n", err)
        os.Exit(2)
    }
    e.Start()
}

传入参数

调整Provider的签名

type Message string

//func NewMessage() Message {
//	return Message("Hi there!")
//}
func NewMessage(phrase string) Message {
	return Message(phrase)
}

调整Injector的签名

func InitializeEvent(phrase string) (Event, error) {
	panic(wire.Build(NewEvent, NewGreeter, NewMessage))
}

不展示生成的代码了,main中的调用

func main() {
	event, err := InitializeEvent("Hi there!")
	if err != nil {
		fmt.Printf("failed to create event: %s\n", err)
		os.Exit(2)
	}

	event.Start()
}

ProviderSet

有时候可能多个类型有相同的依赖,我们每次都将相同的构造器传给wire.Build()不仅繁琐,而且不易维护,一个依赖修改了,所有传入wire.Build()的地方都要修改。为此,wire提供了一个ProviderSet(构造器集合),可以将多个构造器打包成一个集合,后续只需要使用这个集合即可。

如下,后续再调整NewGreeterNewMessage,就可以统一改了

package main

import "github.com/google/wire"

var wireSet = wire.NewSet(NewGreeter, NewMessage)

func InitializeEvent(phrase string) (Event, error) {
	panic(wire.Build(wireSet, NewEvent))
}

func InitializeEvent2(phrase string) (Event, error) {
	panic(wire.Build(wireSet, NewEvent))
}

结构构造器

对于Greeter前面使用NewGreeter作为Provider

type Greeter struct {
	Message Message
}

func NewGreeter(m Message, phrase string) Greeter {
	return Greeter{Message: m}
}

如果不显式实现NewGreeter,可以直接使用wire提供的结构构造器(Struct Provider)。结构构造器创建某个类型的结构,然后用参数或调用其它构造器填充它的字段

结构构造器使用wire.Struct注入,第一个参数固定为new(结构名),后面可接任意多个参数,表示需要为该结构的哪些字段注入值

我们也可以使用通配符*表示注入所有字段

var wireSet = wire.NewSet(NewMessage, wire.Struct(new(Greeter), "Message"))

func InitializeEvent(phrase string) (Event, error) {
	panic(wire.Build(wireSet, NewEvent))
}

结构字段作为构造器

有时候我们编写一个构造器,只是简单的返回某个结构的一个字段,这时可以使用wire.FieldsOf简化操作。

type Foo struct {
    S string
    N int
    F float64
}

func getS(foo Foo) string {
    // Bad! Use wire.FieldsOf instead.
    return foo.S
}

func provideFoo() Foo {
    return Foo{ S: "Hello, World!", N: 1, F: 3.14 }
}

func injectedMessage() string {
    wire.Build(
        provideFoo,
        getS)
    return ""
}
func injectedMessage() string {
    wire.Build(
        provideFoo,
        wire.FieldsOf(new(Foo), "S"))
    return ""
}

同样的,第一个参数为new(结构名),后面跟多个参数表示将哪些字段作为构造器,*表示全部。

绑定值

有时候,我们需要为某个类型绑定一个值,而不想依赖构造器每次都创建一个新的值。有些类型天生就是单例,例如配置,数据库对象(sql.DB)。这时我们可以使用wire.Value绑定值,使用wire.InterfaceValue绑定接口

修改一下 Greeter 使他依赖一个 int 和 io.Reader 然后为它直接绑定 a=10 io.Reader = os.Stdin

// main.go
var singletonMessage = NewMessage("Hello, world!")

type Message string

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

type Greeter struct {
	a int
	r io.Reader

	Message Message
}

func NewGreeter(m Message, phrase string) Greeter {
	return Greeter{Message: m}
}

func (g Greeter) Greet() Message {
	return g.Message
}
var wireSet = wire.NewSet(
	wire.Struct(new(Greeter), "*"),
	wire.Value(10),
	wire.InterfaceValue(new(io.Reader), os.Stdin),
	wire.Value(singletonMessage),
)

func InitializeEvent(phrase string) (Event, error) {
	panic(wire.Build(wireSet, NewEvent))
}

绑定接口

使用 wire.BindStruct 和接口进行绑定,表示这个结构体实现了这个接口,wire.Bind 的使用方法就是 wire.Bind(new(接口), new(实现))

func NewGreeter(m Message, phrase string) Greeter {
	return Greeter{Message: m}
}

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

type IGreeter interface{
	Greet() Message
}

type Event struct {
	Greeter IGreeter
}
var wireSet = wire.NewSet(
	wire.Struct(new(Greeter), "*"),
	wire.Value(10),
	wire.InterfaceValue(new(io.Reader), os.Stdin),
	wire.Value(singletonMessage),
)

func InitializeEvent(phrase string) (Event, error) {
	panic(wire.Build(wireSet, NewEvent, wire.Bind(new(IGreeter), new(*Greeter))))
}

到此这篇关于golang中的依赖注入之wire的文章就介绍到这了,更多相关golang 依赖注入wire内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解Golang中的Mutex并发原语

    详解Golang中的Mutex并发原语

    Mutex 是 Go 语言中互斥锁的实现,它是一种同步机制,用于控制多个 goroutine 之间的并发访问。本文将着重介绍 Go 的 Mutex 并发原语,希望对大家有所帮助
    2023-03-03
  • 详解Go中指针的原理与引用

    详解Go中指针的原理与引用

    在 Go 中,指针是强大而重要的功能,它允许开发人员直接处理内存地址并实现高效的数据操作,本文主要带大家了解下指针在 Go 中的工作原理以及对于编写高效、高性能代码的重要性,希望对大家有所帮助
    2023-09-09
  • golang解析json数据的4种方法总结

    golang解析json数据的4种方法总结

    在日常工作中每一名开发者,不管是前端还是后端,都经常使用 JSON,下面这篇文章主要给大家介绍了关于golang解析json数据的4种方法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • Go语言实现支付宝支付与退款详解

    Go语言实现支付宝支付与退款详解

    本文详细介绍使用Go语言对接支付宝支付与退款功能的步骤和注意事项,包括PC端、WAP端和Android端支付实现,以及退款功能的代码实现,介绍了GoPay库的使用,帮助开发者快速集成支付宝支付到应用中
    2024-10-10
  • Go map底层实现与扩容规则和特性分类详细讲解

    Go map底层实现与扩容规则和特性分类详细讲解

    这篇文章主要介绍了Go map底层实现与扩容规则和特性,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-03-03
  • 详解go中的defer链如何被遍历执行

    详解go中的defer链如何被遍历执行

    为了在退出函数前执行一些资源清理的操作,例如关闭文件、释放连接、释放锁资源等,会在函数里写上多个defer语句,多个_defer 结构体形成一个链表,G 结构体中某个字段指向此链表,那么go中的defer链如何被遍历执行,本文将给大家详细的介绍,感兴趣的朋友可以参考下
    2024-01-01
  • Golang Mutex错过会后悔的重要知识点分享

    Golang Mutex错过会后悔的重要知识点分享

    互斥锁 Mutex 是并发控制的一个基本手段,是为了避免并发竞争建立的并发控制机制,本文主要为大家整理了一些Mutex的相关知识点,希望对大家有所帮助
    2023-07-07
  • Go调度器学习之系统调用详解

    Go调度器学习之系统调用详解

    这篇文章肿,将以一个简单的文件打开的系统调用,来分析一下Go调度器在系统调用时做了什么。文中的示例代码讲解详细,需要的可以参考一下
    2023-04-04
  • go variant底层原理深入解析

    go variant底层原理深入解析

    这篇文章主要为大家介绍了go variant底层原理深入解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • Go语言常见错误接口污染解决分析

    Go语言常见错误接口污染解决分析

    这篇文章主要为大家介绍了Go语言常见错误接口污染解决分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01

最新评论