wire 是個啥米
wire
是一個靜態的注入工具,不像是其他的注入工具(uber/fx
, facebook/inject
)使用 reflect 來達成,他選擇使用 gen code 的方式產生可以使用的注入程式碼。
能夠在編譯階段將注入的動作完成,而不是在執行程式的當下才知道發生了什麼錯誤
Q: Should I use Wire for small applications?
Probably not. Wire is designed to automate more intricate setup code found in larger applications. For small applications, hand-wiring dependencies is simpler. 不複雜的小型專案還是手動注入會更乾淨喔~
go build tag
菜鳥如我這時候才知道,在 .go
檔案最前面加入 build tag 的檔案,在編譯的時候是不會被加進去的(ex: //+build foo
)
所以可以放重複的 function 在同一個 package 裡面,這個操作在 lorca
那篇也有出現過,可以交叉比較一下~
這是一個 wire.go
檔案
//+build wireinject
package main
import (
"learnGo/wire/repo"
"learnGo/wire/service"
"github.com/google/wire"
)
func CreateService() (service.Service, error) {
wire.Build(repo.NewRepo, service.NewService)
panic("never be triggered")
return nil, nil
}
這是一個 wire_gen.go
檔案,他們在同一個 package 下,定義著相同的函式,但是編譯的時候只會看 wire_gen.go
,因為 wire.go
的最前面有標記 build tag
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
import (
"learnGo/wire/repo"
"learnGo/wire/service"
)
import (
_ "github.com/google/wire"
)
// Injectors from wire.go:
func CreateService() (service.Service, error) {
repoRepo := repo.NewRepo()
serviceService := service.NewService(repoRepo)
return serviceService, nil
}
假設我們有個 main.go
來執行…
package main
import (
_ "github.com/google/wire"
)
func main() {
svc, err := CreateService()
if err != nil {
panic(err)
}
svc.SayHello("wire main package")
}
執行 go build -tags wireinject
這樣會使用 wire.go
來編譯,長出來的執行檔就會跑 wire.go:14
的 panic
根據這樣的經驗,進階可以利用 golang build tag
的功能,依照不同的需求來編譯出不同的執行檔
踩到坑: 如果用
go run *.go
來跑,那麼你會得到{function} redeclared in this block
,你的所有檔案還是會被拿來用QQ
Wire 的使用方法
使用上一段落的範例, wire_gen.go
是怎麼長出來的呢?
首先需要安裝 wire
go get github.com/google/wire/cmd/wire
然後輸入
wire
就可以看見 wire_gen.go
長出來囉
詳細看一下內容的前幾行,
// 提醒你和編譯器這是一個產生出來的檔案,不是給人類直接修改的
// Code generated by Wire. DO NOT EDIT.
// 可以使用 `go generate` 指令來觸發後面的指令,
// 在這裡會幫你跑 wire 來 gen code
//go:generate wire
// 註記 build tags 不存在 wireinject 用的
//+build !wireinject
// ...
注入 Interface
在 github 的 document guide 上有寫,很實用也寫一份易讀版
任務: repo.NewRepo 回傳 *repo.DefaultRepo (他實作 repo.Repoer) svc.NewService 需要一個 repo.Repoer 因此我們需要多做一層將 pointer 轉成 interface
// Repo ...
type Repoer interface {
//
}
// NewRepo ...
func NewRepo(cfg *config.Config) *DefultRepo {
return &DefultRepo{
Prefix: cfg.DBCfg.Prefix,
}
}
// NewService ...
func NewService(repo repo.Repoer) *DefultService {
return &DefultService{
repo: repo,
}
}
var Set = wire.NewSet(
// 產生 *DefaultRepo
repo.NewRepo,
// 將 *DefaultRepo 轉成 Repoer
wire.Bind(new(repo.Repo), new(*repo.DefultRepo)),
// 注入需要 Repoer
service.NewService,
)
Ref
https://medium.com/@dche423/master-wire-cn-d57de86caa1b https://github.com/google/wire/blob/main/docs/guide.md