欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang?官方依賴注入工具wire示例詳解

 更新時(shí)間:2022年10月11日 14:59:09   作者:ag9920  
這篇文章主要為大家介紹了Golang?官方依賴注入工具wire示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

依賴注入是什么

Dependency Injection is the idea that your components (usually structs in go) should receive their dependencies when being created.

在 Golang 中,構(gòu)造一個(gè)結(jié)構(gòu)體常見的有兩種方式:

  • 在結(jié)構(gòu)體初始化過程中,構(gòu)建它的依賴;
  • 將依賴作為構(gòu)造器入?yún)?,傳入進(jìn)來。

所謂依賴注入就是第二種思想。不夸張的說,依賴注入是保持我們的軟件系統(tǒng)松耦合,可維護(hù)的最重要的設(shè)計(jì)原則。

為什么?

因?yàn)楫?dāng)你的依賴通過入?yún)魅耄馕吨鴱谋緦?duì)象的角度,你不用去關(guān)心它的生成,只用關(guān)心它的能力。更具體來講,它能讓我們更加傾向于定義好接口,以接口方法來進(jìn)行交互。而不是依賴一個(gè)具體的實(shí)現(xiàn)。

由此而來的另一個(gè)好處在于測試。由于依賴是傳入的,你的系統(tǒng)只管用它的能力,那么具體這個(gè)能力如何實(shí)現(xiàn),其實(shí)是由上層來控制的。我們就可以很方便地進(jìn)行 mock,調(diào)整各個(gè)場景下依賴的實(shí)現(xiàn),來驗(yàn)證我們的 SUT 的表現(xiàn)。

開源選型

Golang 社區(qū)中實(shí)現(xiàn)依賴注入的框架有很多,常用的主要是 google/wire, facebook/inject, uber/dig, uber/fx 等,我們這個(gè)專欄此前就介紹過 goioc/di,大家感興趣的話可以往前翻一下。

大體上看,分為兩個(gè)派系:

  • 代碼生成 codegen
  • 基于反射 reflect

其實(shí)不光是 DI 工具,針對(duì) Golang 這種強(qiáng)類型,但泛型能力較弱的語言,包括 copier,orm 這類通用框架都會(huì)傾向于在這兩個(gè)路徑上二選一。

同樣的,DI 也存在這兩個(gè)排序,上面我們列舉的選項(xiàng)中,facebook/inject, uber/dig, uber/fx,以及我們此前介紹的 goioc/di 都采用了基于反射的解法。這樣的好處在于使用起來相對(duì)直接,不需要額外生成代碼。但劣勢也是相對(duì)的,失去了編譯器檢查的能力,如果注入有問題,只能在運(yùn)行時(shí)報(bào)錯(cuò),啟動(dòng)時(shí)會(huì)存在一些性能消耗。

google/wire 是 Google 官方提出的解決方案,也是業(yè)界目前最經(jīng)典的基于 codegen 來解決依賴注入的開源庫。相較于反射這種在運(yùn)行時(shí)搞事情的操作,wire 需要開發(fā)者提前使用代碼生成工具,觸發(fā)依賴注入代碼的生成,在編譯器干活。相對(duì)的,會(huì)稍微麻煩點(diǎn),但語義更清晰,也消除了運(yùn)行時(shí)的成本。

今天我們就來看看 wire 是怎么用的。

wire

Wire is a code generation tool that automates connecting components using dependency injection. Dependencies between components are represented in Wire as function parameters, encouraging explicit initialization instead of global variables. Because Wire operates without runtime state or reflection, code written to be used with Wire is useful even for hand-written initialization.

wire 在設(shè)計(jì)上受到了 Java’s Dagger 2 的啟發(fā)。正如官方對(duì)它的定位,wire 是一個(gè) Compile-time Dependency Injection for Go (編譯期依賴注入)的代碼生成工具。wire 非常的輕量級(jí),只會(huì)幫助開發(fā)者進(jìn)行按需初始化。

你甚至可以用手寫的初始化代碼來替換它,wire 作為一個(gè)代碼生成工具,僅僅是幫助我們減少注入依賴的繁瑣工作。

一個(gè)經(jīng)典的 DI 函數(shù)簽名類似下面這樣:

// NewUserStore returns a UserStore that uses cfg and db as dependencies.
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}

我們需要生成一個(gè) UserStore,所以需要從函數(shù)入?yún)⒅?,獲取 Config 配置,以及一個(gè) MySQL 的 DB 連接。

思考一下,其實(shí)創(chuàng)建對(duì)象無非是兩種情況:

  • 沒有額外依賴,在當(dāng)前場景下可以直接創(chuàng)建對(duì)象;
  • 存在外部依賴,我們需要先對(duì)外部依賴進(jìn)行構(gòu)建,然后作為參數(shù)傳進(jìn)來,進(jìn)而構(gòu)建當(dāng)前對(duì)象。

所以,要調(diào)用這個(gè) NewUserStore,我們先構(gòu)建兩個(gè)依賴。如果 cfg 和 db 都是第一種情況這種簡單對(duì)象,其實(shí)我們手寫就夠了。

但在生產(chǎn)環(huán)境大型應(yīng)用中,依賴樹的構(gòu)建可能是極其復(fù)雜的。A 依賴 B,B 依賴 C 和 D,C 又依賴 E,這個(gè)鏈路可能很長。這意味著如果手寫,你的初始化代碼會(huì)非常冗余,而且很可能要注意初始化順序。

而且有的依賴可能不僅僅在某一個(gè)父對(duì)象中使用,而是在多個(gè)對(duì)象中共用。這個(gè)過程是非常痛苦的。一句話:

In practice, making changes to initialization code in applications with large dependency graphs is tedious and slow.

那 wire 干的是一件什么事呢?

wire 希望幫助我們搞清楚,到底我要構(gòu)建的這些對(duì)象,存在哪些依賴,如何一步步構(gòu)建出來,保證每個(gè)對(duì)象都能得到它需要的依賴。你不需要考慮這些事情了。

如果要調(diào)整一個(gè)對(duì)象的依賴,我們直接把它的構(gòu)造器從 wire 模板中增加或刪除,或者調(diào)整函數(shù)簽名即可,讓 wire 自己去搞清楚,怎么讓整個(gè) dependency graph 完整。

wire 的設(shè)計(jì)中,需要開發(fā)者理解兩個(gè)概念:providers,injectors。下面我們分別來看看。

providers

Providers 就是我們常說的構(gòu)造器,它們就是一些 Golang 函數(shù),基于一些依賴參數(shù)(也可以沒有),來構(gòu)造出來對(duì)象。我們經(jīng)常用的 NewXXX() XXX 就是經(jīng)典的 Provider,下面是三個(gè)例子:

// NewUserStore is the same function we saw above; it is a provider for UserStore,
// with dependencies on *Config and *mysql.DB.
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
// NewDefaultConfig is a provider for *Config, with no dependencies.
func NewDefaultConfig() *Config {...}
// NewDB is a provider for *mysql.DB based on some connection info.
func NewDB(info *ConnectionInfo) (*mysql.DB, error) {...}

實(shí)際上,我們可能會(huì)需要提供非常多 Provider,畢竟一個(gè)大型項(xiàng)目中涉及的依賴量級(jí)是很大的。所以 wire 提供了 ProviderSet 的概念,用來聚合一組 Provider。拿上面 UserStore 來舉例,我們可以這樣:

var UserStoreSet = wire.ProviderSet(NewUserStore, NewDefaultConfig)

injectors

injectors 也代表了一類函數(shù),和 provider 提供構(gòu)造器不同,它要做的事情在于實(shí)際去注入依賴。

什么?不是說好了 wire 幫我們搞么?怎么還要我們自己寫 injector ?

不要慌,的確是 wire 來做,但 wire 需要我們的幫助才能做到這一點(diǎn)。我們總得告訴 wire 我們想要啥樣的 injector 簽名吧?遇見錯(cuò)誤返回不?要用哪些 provider?

要知道,provider 可不僅僅包括那些簡單的構(gòu)造函數(shù),有些對(duì)象構(gòu)造的時(shí)候需要?jiǎng)e的依賴作為參數(shù),它們自己的構(gòu)造器也是 provider。我們只有告訴 wire 有哪些 provider,它才能知道要給哪些對(duì)象進(jìn)行構(gòu)造。

所以,我們需要在這里做好兩件事:

  • 明確 injector 的函數(shù)簽名,確定好入?yún)ⅲ?/li>
  • 調(diào)用 wire.Build,傳入一系列 provider(或者 providerSet),wire 將會(huì)以此來構(gòu)造最終結(jié)果。
func initUserStore() (*UserStore, error) {
    // We're going to get an error, because NewDB requires a *ConnectionInfo
    // and we didn't provide one.
    wire.Build(UserStoreSet, NewDB)
    return nil, nil  // These return values are ignored.
}

看看示例,發(fā)現(xiàn)了么?

除了這兩步我們什么都不用干,甚至直接 return 了兩個(gè) nil。不要慌,這個(gè)函數(shù)不是最后要用的,wire 會(huì)忽略它的返回值,只需要簽名,以及 wire.Build 這個(gè)信息。最終我們使用的 injector 并不是自己寫的這個(gè)。

好,下來操練一下,首先我們安裝一下 wire 工具:

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

安裝結(jié)束后,直接在當(dāng)前目錄運(yùn)行 wire 即可。輸出如下信息:

$ wire
wire.go:2:10: inject initUserStore: no provider found for ConnectionInfo (required by provider of *mysql.DB)
wire: generate failed

這里信息很明確,上面我們的 func NewDB(info *ConnectionInfo) (*mysql.DB, error) {...} 要求傳入 ConnectionInfo,但是我們調(diào)用 wire.Build 里面沒有對(duì)應(yīng)的 Provider,所以無法生成。

這里我們有兩種方案:

  • 加上 ConnectionInfo 依賴作為參數(shù),表明我們這個(gè)構(gòu)造器,就得顯式傳入;
  • 加上 Provider。

我們?cè)囋嚨谝环N:

func initUserStore(info ConnectionInfo) (*UserStore, error) {
    wire.Build(UserStoreSet, NewDB)
    return nil, nil  // These return values are ignored.
}

只是加了個(gè)入?yún)?,看?wire 能不能識(shí)別出來。再次觸發(fā)命令,會(huì)發(fā)現(xiàn)目錄下多了個(gè) wire_gen.go

// File: wire_gen.go
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
func initUserStore(info ConnectionInfo) (*UserStore, error) {
    defaultConfig := NewDefaultConfig()
    db, err := NewDB(info)
    if err != nil {
        return nil, err
    }
    userStore, err := NewUserStore(defaultConfig, db)
    if err != nil {
        return nil, err
    }
    return userStore, nil
}

完美,原本需要我們手動(dòng)觸發(fā)的流程,wire 全都搞定了。這里的簽名和我們預(yù)期的也一樣。

這里也能看到,wire 其實(shí)非常輕量級(jí),只是把原本需要開發(fā)者手寫的構(gòu)建流程,自動(dòng)生成了。依賴越多,它的作用就越大。

有了生成的代碼,我們就可以繼續(xù)自己的初始化流程,wire 就是個(gè)縮減大家人工的小幫手。

類型區(qū)分

wire不允許不同的組件擁有相同的類型。官方認(rèn)為這是設(shè)計(jì)上的缺陷。我們可以通過類型別名來將組件的類型進(jìn)行區(qū)分。例如服務(wù)會(huì)同時(shí)操作兩個(gè)Redis,redisA, redisB,不要用這樣,wire 無法推導(dǎo)出依賴關(guān)系:

func NewRedisA() *goredis.Client {...}
func NewRedisB() *goredis.Client {...}

建議用:

type RedicCliA *goredis.Client
type RedicCliB *goredis.Client
func NewRedisA() RedicCliA {...}
func NewRedisB() RedicCliB {...}

總結(jié)

這一篇我們只是從理念和基礎(chǔ)用法上帶大家初步理解 wire 的定位,更多用法可以參照官方的 tutorial

使用 wire 可以把性能消耗收斂在編譯期,但隨之而來的代價(jià)就是需要編寫wire.go文件,生成wire_gen.go,且需要為所有struct編寫構(gòu)造函數(shù),而且需要學(xué)習(xí)wire.go的寫法。

以上就是Golang 官方依賴注入工具wire示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Golang 依賴注入wire的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 解決vscode中g(shù)olang插件依賴安裝失敗問題

    解決vscode中g(shù)olang插件依賴安裝失敗問題

    這篇文章主要介紹了解決vscode中g(shù)olang插件依賴安裝失敗問題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-08-08
  • 深入Golang的接口interface

    深入Golang的接口interface

    這篇文章主要介紹了深入Golang的接口interface,go不要求類型顯示地聲明實(shí)現(xiàn)了哪個(gè)接口,只要實(shí)現(xiàn)了相關(guān)的方法即可,編譯器就能檢測到,接下來關(guān)于接口interface的相關(guān)介紹需要的朋友可以參考下面文章內(nèi)容
    2022-06-06
  • Go框架自動(dòng)化工具Beego使用詳解

    Go框架自動(dòng)化工具Beego使用詳解

    這篇文章主要為大家介紹了Go框架自動(dòng)化工具Beego使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Golang中異常處理機(jī)制詳解

    Golang中異常處理機(jī)制詳解

    這篇文章主要給大家介紹了關(guān)于Golang中異常處理機(jī)制的相關(guān)資料,其實(shí)Go語言的異常捕獲要比Python中簡單的多,它沒有Python中那么多復(fù)雜的異常類型及繼承體系,需要的朋友可以參考下
    2021-06-06
  • Go與C語言的互操作實(shí)現(xiàn)

    Go與C語言的互操作實(shí)現(xiàn)

    在Go與C語言互操作方面,Go更是提供了強(qiáng)大的支持。尤其是在Go中使用C,你甚至可以直接在Go源文件中編寫C代碼,本文就詳細(xì)的介紹一下如何使用,感興趣的可以了解一下
    2021-12-12
  • GO項(xiàng)目部署Linux服務(wù)器的實(shí)現(xiàn)示例

    GO項(xiàng)目部署Linux服務(wù)器的實(shí)現(xiàn)示例

    本文主要介紹了GO項(xiàng)目部署Linux服務(wù)器的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-06-06
  • Go語言并發(fā)之Sync包的6個(gè)關(guān)鍵概念總結(jié)

    Go語言并發(fā)之Sync包的6個(gè)關(guān)鍵概念總結(jié)

    這篇文章主要為大家詳細(xì)介紹了Go語言并發(fā)中Sync包的6個(gè)關(guān)鍵概念,文中的示例代碼講解詳細(xì),對(duì)我們深入學(xué)習(xí)Go語言有一定的幫助,需要的可以參考一下
    2023-05-05
  • Go實(shí)現(xiàn)替換(覆蓋)文件某一行內(nèi)容的示例代碼

    Go實(shí)現(xiàn)替換(覆蓋)文件某一行內(nèi)容的示例代碼

    本文主要介紹了Go實(shí)現(xiàn)替換(覆蓋)文件某一行內(nèi)容的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Golang如何構(gòu)造最佳隨機(jī)密碼詳解

    Golang如何構(gòu)造最佳隨機(jī)密碼詳解

    人們往往有使用同一密碼的習(xí)慣,為了防止數(shù)據(jù)庫意外泄露/破壞和出于保護(hù)用戶隱私的目的,下面這篇文章主要給大家介紹了關(guān)于Golang如何構(gòu)造最佳隨機(jī)密碼的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • Golang如何使用go.mod配置加載本地模塊

    Golang如何使用go.mod配置加載本地模塊

    這篇文章主要介紹了Golang如何使用go.mod配置加載本地模塊問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09

最新評(píng)論