1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 8104字,需瀏覽 17分鐘

         ·

        2021-03-19 11:28

        Golang | wire庫

         簡介 

        wire是一個代碼生成工具,它通過自動生成代碼的方式完成依賴注入。

         應(yīng)用場景

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

        Wire核心概念

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

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

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

        安裝




            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ù)的輸入與輸出類型來決定的。

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

        wire包成員的作用

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

        NewSet 

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

        wire.go可以寫成

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

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

        Bind

        Bind函數(shù)的作用是為了讓接口類型參與wire的構(gòu)建過程。wire的構(gòu)建依靠的是參數(shù)的類型來組織代碼,所以接口類型天然是不支持的。Bind函數(shù)通過將接口類型和實現(xiàn)類型綁定,來達(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實現(xiàn)了Fooer接口,需要將最后的new(Foo)改成new(*Foo)

        Struct

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

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

        //等價寫法
        //部分字段初始化
        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)體中的對應(yīng)字段作為Provider,供wire使用。在上面的代碼基礎(chǔ)上,我們做如下的等價

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

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

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

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

        Value

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

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

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

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

        InterfaceValue

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

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

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

        返回值的特殊情況

        返回值 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 
        }

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

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

        清理函數(shù)CleanUp

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

        清理函數(shù)通常作為第二返回值,參數(shù)類型為func(),即為無參數(shù)無返回值的函數(shù)對象。跟error一樣,當(dāng)Provider中的任何一個擁有清理函數(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)有多個Provider有cleanup的時候,wire會自動把cleanup加入到最后的返回函數(shù)中。

        常見問題

        類型重復(fù)

        基礎(chǔ)類型

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

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

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

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

        對象類型重復(fù)

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

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

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

        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
        ...

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

        小結(jié)

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

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


        還想了解更多嗎?

        更多請查看:https://github.com/tidwall/gjson

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


        《酷Go推薦》招募:


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


        大概規(guī)則和每日新聞類似,如果報名人多的話每個人一個月輪到一次,歡迎大家報名!


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



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

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            少妇一级1淫片 | 精品无码久久久久久国产牛牛影视 | 瘦精品无码一区二区三区四区五区六区七区八区 | 成人欧美一区二区三区黑人3p | 无码人妻精品一区二区蜜桃漫画 | 亚洲三级不卡 | chinese麻豆videosex | 超碰91网 | 区一区二区三区中文字幕 | 四虎网站在线 |