wire介绍
wire是google开源的依赖注入框架
依赖注入这个词,学过spring的肯定都不会陌生
在Java当中,是通过spring的ioc容器,底层通过反射机制,帮我们自动实现了依赖注入
在Go当中,除了wire,Go的依赖注入框架还有Uber的dig和Facebook的inject,它们都是使用反射机制来实现运行时依赖注入
但是相较于Java中spring提供的通过注解就把某个类/接口纳入依赖管理
Go没有这么完善的机制,因此需要外界开发的框架,来帮我们写好负责依赖注入的代码
wire实例
type X struct {
Value int
}
func NewX() X {
return X{Value: 10}
}
type Y struct {
Value int
}
func NewY(x X) Y {
return Y{Value: x.Value + 10}
}
type Z struct {
Value int
}
func NewZ(y Y) Z {
return Z{Value: y.Value + 5}
}
可以看出上面的结构体之间,Y依赖X,Z依赖Y
正常来说,我们如果要手动依赖注入,需要在main函数中这么写
// 使用wire之前的初始化方式
x := models.NewX()
y := models.NewY(x)
z := models.NewZ(y)
fmt.Println(z.Value)
我们当然可以在某个函数当中,把这个手动依赖注入的过程封装起来
wire其实就是帮我们自动完成了这一步
这些NewX、NewY、NewZ函数,我们称之为提供者函数
顾名思义,就是用来提供依赖的函数,也就类别Java当中,被注解的类/接口
有了依赖,我们还需要注入,此时需要手动写一个wire.go文件,这就是注入器函数
要声明一个注入器函数只需要在函数体中调用**<font style="color:rgb(68, 68, 68);">wire.Build</font>**
Build方法中,需要传入提供者函数,或者说提供者函数的集合
提供者函数的集合,可以通过wire.NewSet写在提供者函数的文件当中
var ProviderSet = wire.NewSet(NewX, NewY, NewZ)
//go:build wireinject
// +build wireinject
package models
import (
"github.com/google/wire"
)
func InitZ() Z {
// wire.Build(ProviderSet) 通过集合注入
wire.Build(
NewX,
NewY,
NewZ,
)
return Z{}
}
wire的使用
写好了需要被注入的提供者函数(依赖)
也写好了注入器函数
那么就需要使用wire来帮我们自动编写依赖注入的函数了
项目中安装wire命令行工具
go install github.com/google/wire/cmd/wire@latest
在wire.go同级目录下运行下面的命令
wire
wire就会在wire.go同级目录下生成wire_gen.go文件
这个文件就好生成注入器的具体实现
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package models
// Injectors from wire.go:
func InitZ() Z {
x := NewX()
y := NewY(x)
z := NewZ(y)
return z
}
这里我们就可以再一次发现,wire无非就是把我们需要手动写的依赖注入的代码,自动写好封装罢了
封装好之后的初始化方式如下
// 使用wire之后的初始化方式
z2 := models.InitZ()
fmt.Println(z2.Value)
高级特性
这里补充一些我在项目中看到的wire的其他高级用法
整体的总结可以看 https://www.liwenzhou.com/posts/Go/wire/
这里只慢慢列出项目中看见的
1.结构体提供者
可以使用提供的类型构造结构体。使用<font style="color:rgb(68, 68, 68);">wire.Struct</font>
函数构造一个结构体类型,并告诉注入器应该注入哪个字段。
例如,给出以下提供者函数
type Foo int
type Bar int
func ProvideFoo() Foo {/* ... */}
func ProvideBar() Bar {/* ... */}
type FooBar struct {
MyFoo Foo
MyBar Bar
}
注入器函数就可以这样写
func InitFooBar() (*FooBar, error) {
wire.Build(
// 注入Foo
ProvideFoo,
// 注入Bar
ProvideBar,
// 最终需要生成的结构体FooBar
wire.Struct(new(FooBar),"*")
)
return nil,nil
}
这里的返回值不重要,因为wire会根据注入的类型之间的依赖关系,最终生成Struct类型的注入函数
最终生成的FooBar注入器会生成如下
func injectFooBar() FooBar {
foo := ProvideFoo()
bar := ProvideBar()
fooBar := FooBar{
MyFoo: foo,
MyBar: bar,
}
return fooBar,nil
}
这边展示我在项目当中看到的代码,结合web结构,可以更清晰的看到wire的运用
wire.go注入器生成函数
// InitializeController 初始化 Controller
func InitializeController() (*Controller, error) {
wire.Build(
// DAOs
report_dao.NewOnlineReportDao,
report_dao.NewOnlineObjectDao,
// Repository
report_repository.NewReportRepository,
// Service
report_service.NewReportService,
// Manager
app_mananger.NewReportManager,
// Controller
wire.Struct(new(Controller), "*"),
)
return nil, nil
}
可以看到,各种提供者函数来自不同的包,从上往下,下层的函数都依赖上层的函数
最终通过Strut确定需要生成的结构体是Controller
Controller的结构如下
type Controller struct {
reportManager app_mananger.ReportManager
}
需要注入Manager
生成的注入器函数如下
// InitializeController 初始化 Controller
func InitializeController() (*Controller, error) {
onlineReportDao := dao.NewOnlineReportDao()
onlineObjectDao := dao.NewOnlineObjectDao()
reportRepository := repository.NewReportRepository(onlineReportDao, onlineObjectDao)
reportService := service.NewReportService(reportRepository)
reportManager := manager.NewReportManager(reportService)
controller := &Controller{
reportManager: reportManager,
}
return controller, nil
}
我们想要初始化Controller只需要一行代码即可
// 使用依赖注入初始化 Controller
reportController, err := InitializeController()
if err != nil {
panic(err)
}