使用 google-wire 對 Go 進行依賴注入
【導讀】需要依賴注入時,Go 有什麼實現方案?本文詳細介紹了使用 google/wire 庫實現依賴注入的方法。
google/wire 是 Go 語言的編譯時依賴注入框架,與 Spring IoC 一樣,wire 的目的也是讓開發者從對項目中大量依賴的創建和管理中解脫出來,但兩者在實現方式上有着很大的不同。
Go 中的依賴注入
func main() {
NewUserStore(conf.Load(),db.InitMySQL())
}
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
這在小規模項目中效果很好,但當項目規模變大時,單個對象的創建往往需要多個依賴,而這些依賴通常還有它自己的依賴,這就導致對象的創建變得繁瑣,容易出錯。
wire 如何完成依賴注入
在開發中,我們創建對象的過程可以分爲兩步:
- 定義結構體的構造函數
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
- 調用結構體的構造函數進行實例化
NewUserStore(conf.Load(),db.InitMySQL())
第一步我們聲明瞭構造結構體需要的依賴,而 wire 做的,就是 幫我們 “寫” 好第二步的代碼。
依賴聲明
爲了生成第二步中的代碼,我們首先需要將所有的構造函數 (準確的說是所有需要注入的依賴) 進行聲明並傳遞給 wire.Build
方法:
func setup(ctx context.Context) (sv *server.Server, clean func(), err error) {
wire.Build(
conf.Load,
db.InitMySQL,
userstore.NewUserStore,
)
return nil, nil, nil
}
setup
函數的內容最終會被 wire 用下面的實現替換 (接下來會進行說明):
func setup(ctx context.Context) (sv *server.Server, clean func(), err error) {
config := conf.Load()
engine := db.InitMySQL()
userstoreHandler, clean1, err := userstore.NewUserStore(config,engine)
if err != nil {
clean()
return nil, nil, err
}
sv := server.New(userstoreHandler)
return sv, func() { clean1() }, nil
}
可以看到上面 wire 爲我們 “寫” 好的代碼其實跟我們自己將會寫的代碼是一樣的,可以很容易的讀懂。但隨着依賴增多,這樣的代碼就會增加,想象一下,如果項目中有上百個依賴,那麼就會有上百行的 New...(...), if err != nil {...}
代碼。
使用 go generate 生成依賴注入代碼
wire 就是做了這樣一件事:幫我們生成所有需要的對象創建代碼,開發時我們只需要在結構體的構造函數中聲明自己需要什麼。
wire 在實際項目中的使用步驟:
-
通過
wire.Bind
方法進行依賴聲明,假設這部分代碼放在inject.go
文件中 -
使用 go generate http://c.biancheng.net/view/4442.html 命令生成代碼
go generate
需要通過 //go:generate
註釋的方式使用,創建 wire_gen.go
文件並添加該註釋:
//go:generate wire
- 在項目目錄下執行
go generate
命令。
運行該命令時,它將掃描與當前包相關的源代碼文件,找出所有包含 //go:generate
的特殊註釋,提取並執行該特殊註釋後面的命令。
至此,你就可以看到 wire_gen.go
裏已經生成好 func setup(ctx context.Context) (sv *server.Server, clean func(), err error)
方法,方法體爲依賴注入 (對象創建) 代碼。
- 最後一步,在 main 方法中調用
setup
方法 (這裏假設我們的項目是一個提供 RESTful 接口的 HTTP 服務)
package main
func main() {
ctx := context.Background()
srv, cleanup, err := setupGCP(ctx)
if err != nil {
log.Fatalf("failed to setup the server, got error: %s", err)
}
defer cleanup()
log.Fatal(srv.ListenAndServe(":8080"))
}
需要注意的是,如果 inject.go
和 wire_gen.go
在同一個 package 下,那此時 IDE 會提示語法錯誤,因爲兩個文件下存在方法簽名一樣的兩個方法。此時可以使用 //+build go build
構建約束對其中一個文件進行排除。
首先在 inject.go
中加入如下的構建標籤:
//+build wireinject
然後在 wire_gen.go
中加入構建約束使 go build
時排除帶 wireinject
標籤的文件:
//+build !wireinject
使用 wire.Bind, wire.Value 等方法聲明和組織依賴
wire 在 go generate
掃描代碼時從 wire.Bind
中提取項目依賴關係併爲我們生成依賴注入代碼,那我們要怎樣將依賴關係更高效,清晰的 “告知” 給 wire 呢?
wire 提供了幾個函數幫助我們組織和聲明項目中的依賴關係:
-
wire.Bind
: 將接口和其實現進行綁定 -
wire.Value
: 將值 (實例) 包裝爲依賴
轉自:cafebabe
segmentfault.com/a/1190000039060354
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/JLxNf4qX-21XKt34L-yusw