go 的 wire 依赖注入

什么是依赖注入

依赖注入是实现 控制反转 的一种方式,即把功能函数所依赖的其他服务作为入参注入,而非在函数内部声明。
以学生对象为例,该对象具备 db 和 log 两个属性,声明此对象有以下两种方式:

  1. 不使用依赖注入,所需的依赖在函数体内实现:
复制type StudentRepo struct {
	db  *gorm.DB
	log *logrus.Logger
}

func NewStudentRepo() (*StudentRepo, error) {
	db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
	if err != nil {
		return nil, err
	}
	log := logrus.New()
	return &StudentRepo{db: db, log: log}, nil
}

2.使用依赖注入,所需对象由外部传入:

func NewStudentRepo(db *gorm.DB, log *logrus.Logger) *StudentRepo {
	return &StudentRepo{
		db:  db,
		log: log,
	}
}

通过上述示例了解依赖注入后,理解较为抽象的控制反转就更容易了。若要将学生对象 中 db 使用的 sqlite 替换为 mysql,无需修改 repo 层代码,只需在声明对象处进行更改。

控制反转(Inversion of Control,IOC)是一种设计原则,它让我们能够将程序的控制权从程序本身转移到外部容器或框架上。

使用 wire 工具,帮助我们实现依赖注入的代码

首先安装 wire 工具

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

基本概念

  1. Wire 是一个的 Google 开源的依赖注入工具,通过自动生成代码的方式在编译期完成依赖注入。
  2. wire工具,需要两个核心组件: 提供者(provider)注入器(injector),结合下面简单的例子来看
    a. 提供者就是能 new对象 的函数
    b. 注入者就是最后想要的引用函数的模板
  3. wire 命令,必须在 wire.go 所在的同级目录下执行

示例项目

构建一个示例项目,目录结构大致如下:

.
├── go.mod
├── go.sum
├── main.go
├── wire.go
└── wire_gen.go

go 文件

main.go 文件如下

type StudentRepo struct {
	db  *gorm.DB
	log *logrus.Logger
}

type Student struct {
	repo *StudentRepo
}



func NewDb(filePath string) (*gorm.DB, error) {
	return gorm.Open(sqlite.Open(filePath), &gorm.Config{})
}
func NewLog() *logrus.Logger {
	return logrus.New()
}

func NewStudentRepo(db *gorm.DB, log *logrus.Logger) *StudentRepo {
	return &StudentRepo{db: db, log: log}
}

func NewStudent(repo *StudentRepo) *Student {
	return &Student{
		repo: repo,
	}
}

// 使用 wire.NewSet 可以把所有的构建者,放到一个集合中
var ProviderSet = wire.NewSet(NewDb, NewLog, NewStudentRepo, NewStudent)

wire.go 文件如下

//go:build wireinject

package main

import "github.com/google/wire"

// 声明提供者
func InitStudent(sqlitePath string) (*Student, error) {
	wire.Build(ProviderSet)
	return &Student{}, nil
}

  1. wire.go 文件所在的目录,执行 wire 命令,生成 wire_gen.go 文件。
  2. 文件名 wire.go 可以随便定义,wire 命令是根据开头的注释找的,寻找注入器所在的文件,而不是文件名,具体文件标识符的用法,可以参考 https://juejin.cn/post/7126461083837005838
//go:build wireinject
  1. 注入器函数 wire.go,本质就是一个模板,其实不需要完全符合 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 main

// Injectors from wire_ss.go:

// 声明提供者
func InitStudent(sqlitePath string) (*Student, error) {
	db, err := NewDb(sqlitePath)
	if err != nil {
		return nil, err
	}
	logger := NewLog()
	studentRepo := NewStudentRepo(db, logger)
	student := NewStudent(studentRepo)
	return student, nil
}

参考文献

  1. https://juejin.cn/post/7321967859242958857
  2. https://www.liwenzhou.com/posts/Go/wire/
posted @ 2022-12-11 05:47  沧海一声笑rush  阅读(141)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示