<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【GoCN酷Go推薦】依賴注入工具代碼生成器 wire

          共 8104字,需瀏覽 17分鐘

           ·

          2021-03-19 11:28

          Golang | wire庫(kù)

           簡(jiǎn)介 

          wire是一個(gè)代碼生成工具,它通過(guò)自動(dòng)生成代碼的方式完成依賴注入。

           應(yīng)用場(chǎng)景

          wire作為依賴注入的代碼生成工具,非常適合復(fù)雜對(duì)象的創(chuàng)建。而在大型項(xiàng)目中,擁有一個(gè)合適的依賴注入的框架將使得項(xiàng)目的開(kāi)發(fā)與維護(hù)十分便捷。

          Wire核心概念

          wire 中最核心的兩個(gè)概念就是Injector和Provider。

          Provider : 生成組件的普通方法。這些方法接收所需依賴作為參數(shù),創(chuàng)建組件并將其返回

          Injector : 代表了我們最終要生成的構(gòu)建函數(shù)的函數(shù)簽名,返回值代表了構(gòu)建的目標(biāo),在最后生成的代碼中,此函數(shù)簽名會(huì)完整的保留下來(lái)。

          安裝




              go get github.com/google/wire/cmd/wire

           代碼生成

          命令行在指定目錄下執(zhí)行 wire命令即可。

          示例學(xué)習(xí)

          https://github.com/google/wire/tree/main/internal/wire/testdata/

           成員介紹


          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

          基礎(chǔ)代碼

          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
          }

          這里我們可以看到代碼的生成是根據(jù)wire.Build參數(shù)的輸入與輸出類型來(lái)決定的。

          wire.Build的參數(shù)是Provider的不定長(zhǎng)列表。

          wire包成員的作用

          wire的成員每一個(gè)都是為了Provider服務(wù)的,他們各自有適用的場(chǎng)景。

          NewSet 

          NewSet的作用是為了防止Provider過(guò)多導(dǎo)致混亂,它把一組業(yè)務(wù)相關(guān)的Provider放在一起組織成ProviderSet。

          wire.go可以寫(xiě)成

          var NewBranchSet = wire.NewSet(NewLeaf,NewBranch)
          func InitRoot(name string) Root {
              wire.Build(NewBranchSet,NewRoot)
              return Root{}
          }

          值得注意的事,NewSet可以寫(xiě)在原結(jié)構(gòu)體所在的文件中,以方便切換和維護(hù)。

          Bind

          Bind函數(shù)的作用是為了讓接口類型參與wire的構(gòu)建過(guò)程。wire的構(gòu)建依靠的是參數(shù)的類型來(lái)組織代碼,所以接口類型天然是不支持的。Bind函數(shù)通過(guò)將接口類型和實(shí)現(xiàn)類型綁定,來(lái)達(dá)到依賴注入的目的。

          type Fooer interface{
              HelloWorld() 
          }
          type Foo struct{}
          func (f Foo)HelloWorld(){}

          var bind = wire.Bind(new(Fooer),new(Foo))

          示例地址:https://github.com/google/wire/tree/main/internal/wire/testdata/BindInjectorArgPointer

          這樣將bind傳入NewSet或Build中就可以將Fooer接口和Foo類型綁定。

          這里需要特別注意,如果是*Foo實(shí)現(xiàn)了Fooer接口,需要將最后的new(Foo)改成new(*Foo)

          Struct

          Struct函數(shù)用于簡(jiǎn)化結(jié)構(gòu)體的Provider,當(dāng)結(jié)構(gòu)體的Provider僅僅是字段賦值時(shí)可以使用這個(gè)函數(shù)。

          //當(dāng)Leaf中成員變量很多時(shí),或者只需要部分初始化時(shí),構(gòu)造函數(shù)會(huì)變得很復(fù)雜
          func NewLeaf(name string) Leaf {return Leaf{Name:name}}

          //等價(jià)寫(xiě)法
          //部分字段初始化
          wire.Struct(new(Leaf),"Name")
          //全字段初始化
          wire.Struct(new(Leaf),"*")

          這里的NewLeaf函數(shù)可以被下面的部分字段初始化函數(shù)替代。

          Struct函數(shù)可以作為Provider出現(xiàn)在Build或NewSet的參數(shù)中。

          FieldsOf

          FieldsOf函數(shù)可以將結(jié)構(gòu)體中的對(duì)應(yīng)字段作為Provider,供wire使用。在上面的代碼基礎(chǔ)上,我們做如下的等價(jià)

          //獲得Leaf中Name字段的Provider
          func NewName(l Leaf) string {return l.Name}

          //等價(jià)寫(xiě)法
          //FieldsOf的方式獲得結(jié)構(gòu)體內(nèi)的字段
          wire.FieldsOf(new(Leaf),"Name")

          示例地址:https://github.com/google/wire/tree/main/internal/wire/testdata/FieldsOfStruct

          這里的代碼是等價(jià)的,但是卻不能和上面的代碼共存,原因稍后會(huì)解釋。

          Value

          Value函數(shù)為基本類型的屬性綁定具體值,在基于需求的基礎(chǔ)上簡(jiǎn)化代碼。

          func NewLeaf()Leaf{
              return Leaf{
                  Name:"leaf",
              }
          }

          //等價(jià)寫(xiě)法
          wire.Value(Leaf{Name:"leaf"})

          以上兩個(gè)函數(shù)在作為Provider上也是等價(jià)的,可以出現(xiàn)在Build或NewSet中。

          InterfaceValue

          InterfaceValue作用與Value函數(shù)類似,只是InterfaceValue函數(shù)是為接口類型綁定具體值。

          wire.InterfaceValue(new(io.Reader),os.Stdin)

          比較少用到,這里就不細(xì)講了。

          返回值的特殊情況

          返回值 error

          wire是支持返回對(duì)象的同時(shí)攜帶error的。對(duì)于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 
          }

          示例地址:https://github.com/google/wire/tree/main/internal/wire/testdata/ReturnError

          可以看到當(dāng)Provider中出現(xiàn)error的返回值時(shí),Injector函數(shù)的返回值中也必須攜帶error的返回值

          清理函數(shù)CleanUp

          清理通常出現(xiàn)在有文件對(duì)象,socket對(duì)象參與的構(gòu)建函數(shù)中,無(wú)論是出錯(cuò)后的資源關(guān)閉,還是作為正常獲得對(duì)象后的析構(gòu)函數(shù)都是有必要的。

          清理函數(shù)通常作為第二返回值,參數(shù)類型為func(),即為無(wú)參數(shù)無(wú)返回值的函數(shù)對(duì)象。跟error一樣,當(dāng)Provider中的任何一個(gè)擁有清理函數(shù),Injector的函數(shù)簽名返回值中也必須包含該函數(shù)類型。

          //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()
              }   
          }

          示例地址:https://github.com/google/wire/tree/main/internal/wire/testdata/Cleanup

          就這樣名為cleanup的清理函數(shù)就隨著InitRoot返回了。當(dāng)有多個(gè)Provider有cleanup的時(shí)候,wire會(huì)自動(dòng)把cleanup加入到最后的返回函數(shù)中。

          常見(jiàn)問(wèn)題

          類型重復(fù)

          基礎(chǔ)類型

          基礎(chǔ)類型是構(gòu)建結(jié)構(gòu)體的基礎(chǔ),其作為參數(shù)創(chuàng)建結(jié)構(gòu)體是十分常見(jiàn)的,參數(shù)類型重復(fù)更是不可避免的。wire通過(guò)Go語(yǔ)言語(yǔ)法中的"type A B"的方法來(lái)解決詞類問(wèn)題。

          //wire.go
          type Account string
          func InitRoot(name string, account Account) (Root, func()) {...}
          出現(xiàn)在wire.go中的"type A B" 會(huì)自動(dòng)復(fù)制到wire_gen.go中

          示例地址:https://github.com/google/wire/tree/main/internal/wire/testdata/InjectInput

          個(gè)人觀點(diǎn) wire著眼于復(fù)雜對(duì)象的構(gòu)建,因此基礎(chǔ)類型的屬性賦值推薦使用結(jié)構(gòu)體本身的Set操作完成。

          對(duì)象類型重復(fù)

          每一個(gè)Provider都是一個(gè)組件的生成方法,如果有兩個(gè)Provider生成同一類組件,那么在構(gòu)建過(guò)程中就會(huì)產(chǎn)生沖突,這里需要特別注意,保證組件的類型唯一性。

          循環(huán)構(gòu)建

          循環(huán)構(gòu)建指的是多個(gè)Provider相互提供參數(shù)和返回值形成一個(gè)閉環(huán)。當(dāng)wire檢查構(gòu)建的流程含有閉環(huán)構(gòu)建的時(shí)候,就會(huì)報(bào)錯(cuò)。

          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) //錯(cuò)誤 cycle for XXX
          ...

          示例地址:https://github.com/google/wire/tree/main/internal/wire/testdata/Cycle

          小結(jié)

          wire是一個(gè)強(qiáng)大的工具,它在不運(yùn)行Go程序的基礎(chǔ)上,借助于特定文件("http://+build wireinject")的解析,自動(dòng)生成對(duì)象的構(gòu)造函數(shù)代碼。

          Go語(yǔ)言工程化的過(guò)程中,涉及到諸多對(duì)象的包級(jí)別歸類,wire可以很好的協(xié)助我們完成復(fù)雜對(duì)象的構(gòu)建過(guò)程。


          還想了解更多嗎?

          更多請(qǐng)查看:https://github.com/tidwall/gjson

          歡迎加入我們GOLANG中國(guó)社區(qū):https://gocn.vip/


          《酷Go推薦》招募:


          各位Gopher同學(xué),最近我們社區(qū)打算推出一個(gè)類似GoCN每日新聞的新欄目《酷Go推薦》,主要是每周推薦一個(gè)庫(kù)或者好的項(xiàng)目,然后寫(xiě)一點(diǎn)這個(gè)庫(kù)使用方法或者優(yōu)點(diǎn)之類的,這樣可以真正的幫助到大家能夠?qū)W習(xí)到新的庫(kù),并且知道怎么用。


          大概規(guī)則和每日新聞?lì)愃疲绻麍?bào)名人多的話每個(gè)人一個(gè)月輪到一次,歡迎大家報(bào)名!


          點(diǎn)擊 閱讀原文 即刻報(bào)名



          瀏覽 52
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  在线观看黄色免费视频 | 无码人妻AV | 可以免费看的A片 | 欧美操逼网| 影音先锋男人的资源网站 |