前言
由于之前项目中各个组件都是通过全局变量传递的,随着项目的推进,一大堆全局变量会混成一团,如果一不小心在某个地方对全局变量进行修改将可能引发严重的 panic
,便打算使用依赖注入重构一下, Wire
是一个灵活的依赖注入工具,能够帮助我们在程序编译期就完成依赖注入。
依赖注入简述
依赖注入是指组件在创建时,就应该获取该组件所依赖的其他关系,如下代码所示,要创建一个 App
实例需要 Config
、 Server
结构体,分别展示使用与未使用依赖注入的两种方式。
type App struct {
conf *Config
httpServer *Server
}
// 构建 Config
func InitConfig() *Config {
return &Config{}
}
// 构建 Server
func NewHttpServer() *Server {
return &Server{}
}
// 构建 App (内部调用依赖组件的构建函数)
func NewApp() *App {
return &App{
conf: InitConfig(),
httpServer: NewHttpServer(),
}
}
// 构建 App (需由调用者传入依赖组件)
func NewAppByDI(conf *Config, httpSrv *Server) *App {
return &App{
conf: conf,
httpServer: httpSrv,
}
}
func main() {
// 未使用依赖注入
app := NewApp()
// 使用依赖注入
config := InitConfig()
server := NewHttpServer()
app := NewAppByDI(config, server)
}
- • 未使用依赖注入的情况下,调用者无法知道
App
内部使用了Config
和Server
,如果Config
构建函数InitConfig()
发生变化,假设需要增加一个参数,我们就需要在每个调用InitConfig()
的地方修改代码。 - • 使用依赖注入的情况下,将
Config
和Server
的构建逻辑与构建App
的逻辑分离开,即使Config
的构建函数InitConfig
发生了变化,也只需要修改一处代码,但是在构建App
之前,需要手动构建Config
和Server
,随着程序推进,这些依赖关系将越来越复杂,这时候就需要依赖注入工具Wire
来帮助我们生成依赖关系。
安装工具
代码语言:javascript复制# 导入项目
go get -u github.com/google/wire
# 安装命令
go install github.com/google/wire/cmd/wire
工作原理简述
Wire
有两个核心概念 Provider
和 Injector
。
Provider
就是各组件的构建函数,这些函数需要接收依赖的组件为参数,创建组件并返回,就是上面例子中的 NewAppByDI()
函数。
Injector
是由 Wire
自动生成的函数,它会根据依赖的顺序调用 Provider
,为了生成该函数,通常在 wire.go
文件中定义 Injector
函数签名,在其函数内部调用 wire.Build()
,并以所需 Provider
作为参数(无须考虑顺序)。
重构
这里只简单展示一下 wire.go
文件,主要工作就是先为之前项目中使用的各个组件都编写 Provider
函数,然后在 wire.Build()
中传入各个 Provider
即可,完整代码请查看 https://github.com/jassue/jassue-gin/tree/main。
//go:build wireinject
// build wireinject
package main
import (
"github.com/google/wire"
"github.com/jassue/gin-wire/app/compo"
"github.com/jassue/gin-wire/app/cron"
"github.com/jassue/gin-wire/app/data"
"github.com/jassue/gin-wire/app/handler"
"github.com/jassue/gin-wire/app/service"
"github.com/jassue/gin-wire/config"
"github.com/jassue/gin-wire/router"
"go.uber.org/zap"
"gopkg.in/natefinch/lumberjack.v2"
)
// wireApp init application.
func wireApp(*config.Configuration, *lumberjack.Logger, *zap.Logger) (*App, func(), error) {
panic(wire.Build(data.ProviderSet, compo.ProviderSet, service.ProviderSet, handler.ProviderSet, router.ProviderSet, cron.ProviderSet, newHttpServer, newApp))
}