依賴注入工具程式碼生成器 wire
Golang | wire 庫
推薦編輯:不落凡塵
簡介
wire 是一個程式碼生成工具,它通過自動生成程式碼的方式完成依賴注入。
應用場景
wire 作為依賴注入的程式碼生成工具,非常適合複雜物件的建立。而在大型專案中,擁有一個合適的依賴注入的框架將使得專案的開發與維護十分便捷。
Wire 核心概念
wire 中最核心的兩個概念就是 Injector 和 Provider。
Provider : 生成元件的普通方法。這些方法接收所需依賴作為引數,建立元件並將其返回
Injector : 代表了我們最終要生成的構建函式的函式簽名,返回值代表了構建的目標,在最後生成的程式碼中,此函式簽名會完整的保留下來。
安裝
go get github.com/google/wire/cmd/wire
程式碼生成
命令列在指定目錄下執行 wire
命令即可。
示例學習
成員介紹
func NewSet(...interface{}) ProviderSet
func Build(...interface{}) string
func Bind(iface, to interface{}) Binding
func Struct(structType interface{}, fieldNames ...string) StructProvider
func FieldsOf(structType interface{}, fieldNames ...string) StructFields
func Value(interface{}) ProvidedValue
func InterfaceValue(typ interface{}, x interface{}) ProvidedValue
基礎程式碼
main.go
package main
type Leaf struct {
Name string
}
type Branch struct{
L Leaf
}
type Root struct {
B Branch
}
func NewLeaf(name string) Leaf {return Leaf{Name:name}}
func NewBranch(l Leaf) Branch {return Branch{L:l}}
func NewRoot(b Branch) Root {return Root{B:b}}
wire.go
// +build wireinject
// The build tag makes sure the stub is not built in the final build.
package main
import (
"github.com/google/wire"
)
func InitRoot(name string) Root {
wire.Build(NewLeaf,NewBranch,NewRoot)
return Root{}
}
wire_gen.go
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func InitRoot(name string) Root {
leaf := NewLeaf(name)
branch := NewBranch(leaf)
root := NewRoot(branch)
return root
}
這裡我們可以看到程式碼的生成是根據 wire.Build 引數的輸入與輸出型別來決定的。
wire.Build 的引數是 Provider 的不定長列表。
wire 包成員的作用
wire 的成員每一個都是為了 Provider 服務的,他們各自有適用的場景。
NewSet
NewSet 的作用是為了防止 Provider 過多導致混亂,它把一組業務相關的 Provider 放在一起組織成 ProviderSet。
wire.go 可以寫成
var NewBranchSet = wire.NewSet(NewLeaf,NewBranch)
func InitRoot(name string) Root {
wire.Build(NewBranchSet,NewRoot)
return Root{}
}
值得注意的事,NewSet 可以寫在原結構體所在的檔案中,以方便切換和維護。
Bind
Bind 函式的作用是為了讓介面型別參與 wire 的構建過程。wire 的構建依靠的是引數的型別來組織程式碼,所以介面型別天然是不支援的。Bind 函式通過將介面型別和實現型別繫結,來達到依賴注入的目的。
type Fooer interface{
HelloWorld()
}
type Foo struct{}
func (f Foo)HelloWorld(){}
var bind = wire.Bind(new(Fooer),new(Foo))
這樣將 bind 傳入 NewSet 或 Build 中就可以將 Fooer 介面和 Foo 型別繫結。
這裡需要特別注意,如果是 *Foo 實現了 Fooer 介面,需要將最後的 new(Foo) 改成 new(*Foo)
Struct
Struct 函式用於簡化結構體的 Provider,當結構體的 Provider 僅僅是欄位賦值時可以使用這個函式。
//當Leaf中成員變數很多時,或者只需要部分初始化時,建構函式會變得很複雜
func NewLeaf(name string) Leaf {return Leaf{Name:name}}
//等價寫法
//部分欄位初始化
wire.Struct(new(Leaf),"Name")
//全欄位初始化
wire.Struct(new(Leaf),"*")
這裡的 NewLeaf 函式可以被下面的部分欄位初始化函式替代。
Struct 函式可以作為 Provider 出現在 Build 或 NewSet 的引數中。
FieldsOf
FieldsOf 函式可以將結構體中的對應欄位作為 Provider,供 wire 使用。 在上面的程式碼基礎上,我們做如下的等價
//獲得Leaf中Name欄位的Provider
func NewName(l Leaf) string {return l.Name}
//等價寫法
//FieldsOf的方式獲得結構體內的欄位
wire.FieldsOf(new(Leaf),"Name")
這裡的程式碼是等價的,但是卻不能和上面的程式碼共存,原因稍後會解釋。
Value
Value 函式為基本型別的屬性繫結具體值,在基於需求的基礎上簡化程式碼。
func NewLeaf()Leaf{
return Leaf{
Name:"leaf",
}
}
//等價寫法
wire.Value(Leaf{Name:"leaf"})
以上兩個函式在作為 Provider 上也是等價的,可以出現在 Build 或 NewSet 中。
InterfaceValue
InterfaceValue 作用與 Value 函式類似,只是 InterfaceValue 函式是為介面型別繫結具體值。
wire.InterfaceValue(new(io.Reader),os.Stdin)
比較少用到,這裡就不細講了。
返回值的特殊情況
返回值 error
wire 是支援返回物件的同時攜帶 error 的。對於 error 型別的返回值,wire 也能很好的處理。
//main.go
func NewLeaf(name string) (Leaf, error) { return Leaf{Name: name}, nil }
//wire.go
func InitRoot(name string) (Root, error) {
...
}
//wire_gen.go
func InitRoot(name string) (Root, error) {
leaf, err := NewLeaf(name)
if err != nil {
return Root{}, err
}
branch := NewBranch(leaf)
root := NewRoot(branch)
return root, nil
}
可以看到當 Provider 中出現 error 的返回值時,Injector 函式的返回值中也必須攜帶 error 的返回值
清理函式 CleanUp
清理通常出現在有檔案物件,socket 物件參與的構建函式中,無論是出錯後的資源關閉,還是作為正常獲得物件後的解構函式都是有必要的。
清理函式通常作為第二返回值,引數型別為 func(),即為無引數無返回值的函式物件。跟 error 一樣,當 Provider 中的任何一個擁有清理函式,Injector 的函式簽名返回值中也必須包含該函式型別。
//main.go
func NewLeaf(name string) (Leaf, func()) {
r := Leaf{Name: name}
return r, func() { r.Name = "" }
}
func NewBranch(l Leaf) (Branch, func()) { return Branch{L: l}, func() {} }
//wire.go
func InitRoot(name string) (Root, func()) {...}
//wire_gen.go
func InitRoot(name string) (Root, func()) {
leaf, cleanup := NewLeaf(name)
branch, cleanup2 := NewBranch(leaf)
root := NewRoot(branch)
return root, func() {
cleanup2()
cleanup()
}
}
就這樣名為 cleanup 的清理函式就隨著 InitRoot 返回了。當有多個 Provider 有 cleanup 的時候,wire 會自動把 cleanup 加入到最後的返回函式中。
常見問題
型別重複
基礎型別
基礎型別是構建結構體的基礎,其作為引數建立結構體是十分常見的,引數型別重複更是不可避免的。wire 通過 Go 語言語法中的"type A B"的方法來解決詞類問題。
//wire.go
type Account string
func InitRoot(name string, account Account) (Root, func()) {...}
出現在 wire.go 中的"type A B" 會自動複製到 wire_gen.go 中
個人觀點 wire 著眼於複雜物件的構建,因此基礎型別的屬性賦值推薦使用結構體本身的 Set 操作完成。
物件型別重複
每一個 Provider 都是一個元件的生成方法,如果有兩個 Provider 生成同一類元件,那麼在構建過程中就會產生衝突,這裡需要特別注意,保證元件的型別唯一性。
迴圈構建
迴圈構建指的是多個 Provider 相互提供引數和返回值形成一個閉環。 當 wire 檢查構建的流程含有閉環構建的時候,就會報錯。
type Root struct{
B Branch
}
type Branch struct {
L Leaf
}
type Leaf struct {
R Root
}
func NewLeaf(r Root) Leaf {return Leaf{R:r}}
func NewBranch(l Leaf) Branch {return Branch{L:l}}
func NewRoot(b Branch) Root {return Root{B:b}}
...
wire.Build(NewLeaf,NewRranch,NewRoot) //錯誤 cycle for XXX
...
小結
wire 是一個強大的工具,它在不執行 Go 程式的基礎上,藉助於特定檔案 ("//+build wireinject") 的解析,自動生成物件的建構函式程式碼。
Go 語言工程化的過程中,涉及到諸多物件的包級別歸類,wire 可以很好的協助我們完成複雜物件的構建過程。
還想了解更多嗎?
更多請檢視: https://github.com/google/wire
歡迎加入我們 GOLANG 中國社群:https://gocn.vip
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- Go 官方依賴注入工具wireGo依賴注入
- 使用google wire解決依賴注入Go依賴注入
- Golang 依賴注入設計哲學|12.6K 🌟 的依賴注入庫 wireGolang依賴注入
- Go中使用Google Wire實現依賴注入Go依賴注入
- Go 專案依賴注入wire工具最佳實踐介紹與使用Go依賴注入
- 搞定Go單元測試(四)—— 依賴注入框架(wire)Go依賴注入框架
- javascript 依賴注入程式碼例項JavaScript依賴注入
- [譯]使用Go Cloud的Wire進行編譯時依賴注入GoCloud編譯依賴注入
- Laravel 依賴注入原始碼解析Laravel依賴注入原始碼
- angular依賴注入Angular依賴注入
- XUnit 依賴注入依賴注入
- Struts 依賴注入依賴注入
- [譯] 用依賴注入解耦你的程式碼依賴注入解耦
- 依賴倒置(DIP)與依賴注入(DI)依賴注入
- 依賴注入?依賴注入是如何實現解耦的?依賴注入解耦
- 200行Java程式碼實現依賴注入框架Java依賴注入框架
- ASP.NET Core中的依賴注入(2):依賴注入(DI)ASP.NET依賴注入
- [譯] 依賴注入?? 哈??依賴注入
- Angular 依賴注入原理Angular依賴注入
- .Net Core — 依賴注入依賴注入
- 理解 Angular 依賴注入Angular依賴注入
- Spring依賴注入Spring依賴注入
- Spring依賴注入---Spring依賴注入
- 依賴注入系列教程依賴注入
- 我看依賴注入依賴注入
- webapi - 使用依賴注入WebAPI依賴注入
- 依賴注入是否值得?依賴注入
- Spring原始碼系列:依賴注入(三)-屬性注入Spring原始碼依賴注入
- React 原始碼中的依賴注入方法React原始碼依賴注入
- 死磕Spring原始碼-依賴注入Spring原始碼依賴注入
- Spring原始碼解析——依賴注入(二)Spring原始碼依賴注入
- Spring原始碼系列:依賴注入-引言Spring原始碼依賴注入
- 面向切面程式設計和依賴注入程式設計依賴注入
- 十幾行程式碼實現一個ts依賴注入行程依賴注入
- [靈性程式設計]GO的依賴注入 AND 自動生成程式碼程式設計Go依賴注入
- Asp .Net Core 依賴注入依賴注入
- Spring IOC——依賴注入Spring依賴注入
- 入門系列-依賴注入依賴注入