Golang-Wire框架学习(Java转型)

发布于:2025-09-04 ⋅ 阅读:(20) ⋅ 点赞:(0)

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)
}

网站公告

今日签到

点亮在社区的每一天
去签到