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>

        Go 依賴注入:為什么把 dig 遷移到 wire

        共 3049字,需瀏覽 7分鐘

         ·

        2021-07-13 22:19

        digwire都是Go依賴注入的工具,那么,本質(zhì)上功能相似的工具,為什么要從dig切換成 wire

        場景

        我們從場景出發(fā)。假設(shè)我們的項(xiàng)目分層是:

        router->controller->service->dao。

        目錄大概就長這樣:

        現(xiàn)在我們需要對外暴露一個(gè)訂單服務(wù)的接口。

        首頁看main.go文件

        這里使用了gin啟動(dòng)項(xiàng)目。


        dig

        然后我們查看dig.ContainerByDig(),

        通過dig.New()創(chuàng)建一個(gè)di容器Provide函數(shù)用于添加服務(wù)提供者,Provide函數(shù)第一個(gè)參數(shù)本質(zhì)上是個(gè)函數(shù)。一個(gè)告訴容器 "我能提供什么,為了提供它,我需要什么?" 的函數(shù)。

        我們看上面的server.NewOrderServer,

        這里的NewOrderServer(xxx)Provide中的語意就是 "我能提供一個(gè)OrderServerInterface服務(wù),但是我需要依賴一個(gè)dao.OrderDao"。

        剛才的代碼中,

        因?yàn)槲覀兊恼{(diào)用鏈?zhǔn)?/span>controller->server->dao,那么本質(zhì)上他們的依賴是controller<-server<-dao,只是依賴的不是具體的實(shí)現(xiàn),而是抽象的接口。

        所以你看到Provide是按照依賴關(guān)系順序?qū)懙?/span>。

        其實(shí)完全沒有必要,因?yàn)檫@一步dig只會對這些函數(shù)進(jìn)行分析,提取函數(shù)的形參以及返回值。然后根據(jù)返回的參數(shù)來組織容器結(jié)構(gòu)。并不會在這一步執(zhí)行傳入的函數(shù),所以在Provide階段前后順序并不重要,只要確保不遺漏依賴項(xiàng)即可。

        萬事俱備,我們開始注冊一個(gè)能獲取訂單的路由,

        此時(shí),調(diào)用invoke, 才是真正需要獲取*controller.OrderHandler對象。

        調(diào)用invoke方法,會對傳入的參數(shù)做分析,參數(shù)中存在handle *controller.OrderHandler,就會去容器中尋找哪個(gè)Provide進(jìn)來的函數(shù)返回類型是handle *controller.OrderHandler,

        然后對應(yīng)找到,

        發(fā)現(xiàn)這個(gè)函數(shù)有形參server.OrderServerInterface,那就去找對應(yīng)返回此類型的函數(shù),

        又發(fā)現(xiàn)形參(order dao.OrderDao),

        最后發(fā)現(xiàn)NewOrderDao沒有依賴,不需要再查詢依賴。

        開始執(zhí)行函數(shù)的調(diào)用NewOrderDao(),把返回的OrderDao

        傳入到上層NewOrderServer(order dao.OrderDao)進(jìn)行函數(shù)調(diào)用,

        NewOrderServer(order dao.OrderDao)返回的

        OrderServerInterface繼續(xù)返回到上層

        NewOrderHandler(server server.OrderServerInterface)

        執(zhí)行調(diào)用,最后再把函數(shù)調(diào)用返回的*OrderHandler傳遞給

        dig.Invoke(func(handle *controller.OrderHandler) {},

        整個(gè)鏈路就通了。上面看的可能不太舒服,用一個(gè)圖來描述這個(gè)過程


        dig的整個(gè)流程采用的是反射機(jī)制,在運(yùn)行時(shí)計(jì)算依賴關(guān)系,構(gòu)造依賴對象。

        這樣會存在什么問題?

        假設(shè)我現(xiàn)在注釋掉Provide的一行代碼,比如,

        我們在編譯項(xiàng)目的時(shí)候并不會報(bào)任何錯(cuò)誤,只會在運(yùn)行時(shí)才發(fā)現(xiàn)缺少了依賴項(xiàng)

         


        wire

        還是上面的代碼,我們使用wire作為我們的DI容器。

        wire也有兩個(gè)核心概念:ProviderInjector。

        其中Provider的概念和dig的概念是一樣的:"我能提供什么?我需要什么依賴"。

        比如下面wire.go中的代碼,

        dao.NewOrderDaoserver.NewOrderServer以及controller.NewOrderHandler就是Provider。

        你會發(fā)現(xiàn)這里還調(diào)用wire.NewSet把他們整合在一起,賦值給了一個(gè)變量orderSet。

        其實(shí)是用到ProviderSet的概念。原理就是把一組相關(guān)的Provider進(jìn)行打包。

        這樣的好處是:

        • 結(jié)構(gòu)依賴清晰,便于閱讀

        • 以組的形式,減少injector里的Build。

        至于injector,本質(zhì)上就是按照依賴關(guān)系調(diào)用Provider的函數(shù),然后最終生成我們想要的對象(服務(wù))。

        比如上面的ContainerByWire()就是一個(gè)injector。

        那么wire.go文件整體的思路就是:定義好injector然后實(shí)現(xiàn)所需的Provider。

        最后在當(dāng)前wire.go文件夾下執(zhí)行wire命令后,

        此時(shí)如果你的依賴項(xiàng)存在問題,那么就會報(bào)錯(cuò)提示。比如我現(xiàn)在隱藏上面的dao.NewOrderDao,那么會出現(xiàn) ,


        如果依賴不存在問題,最終會生成一個(gè)wire_gen.go文件。

        需要注意上面兩個(gè)文件。我們看到wire.go中第一行//+build wireinject,這個(gè)build tag確保在常規(guī)編譯時(shí)忽略wire.go文件。 

        而與之相對的wire_gen.go中的//+build !wireinject。 

        兩個(gè)對立的build tag是為了確保在任意情況下,兩個(gè)文件只有一個(gè)文件生效, 避免出現(xiàn) "ContainerByWire()方法被重新定義" 的編譯錯(cuò)誤。

        現(xiàn)在我們可以真正使用injector了,我們在入口文件中替換成dig。


        一切正常。

        當(dāng)然wire有一個(gè)點(diǎn)需要注意wire.go文件中開頭幾行:

        build tagpackage他們之間是有空行的,如果沒有空行,build tag識別不了,那么編譯的時(shí)候就會報(bào)重復(fù)聲明的錯(cuò)誤 

        還有很多高級的操作可以自行了解如果有需要完整代碼請下方留言。

        總結(jié)

        以上大體介紹了 go digwire兩個(gè)DI工具。其中dig是通過運(yùn)行時(shí)反射實(shí)現(xiàn)的依賴注入。 而wire是根據(jù)自定義的代碼,通過命令,生成相應(yīng)的依賴注入代碼,在編譯期就完成依賴注入,無需反射機(jī)制。 這樣的好處是

        • 方便排查,如果存在依賴錯(cuò)誤,編譯時(shí)就能發(fā)現(xiàn)。而 dig 只能在運(yùn)行時(shí)才能發(fā)現(xiàn)依賴錯(cuò)誤。

        • 避免依賴膨脹,wire生成的代碼只包含被依賴的,而dig可能會存在好多無用依賴。

        • 依賴關(guān)系靜態(tài)存在源碼,便于工具分析。



        Reference

        [1]https://github.com/google/wire

        [2]

        https://github.com/uber-go/dig

        [3]

        https://medium.com/@dche423/master-wire-cn-d57de86caa1b

        [4]

        https://www.cnblogs.com/li-peng/p/14708132.html




        推薦閱讀


        福利

        我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        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>
            欧美黄网在线观看 | 日韩一区在线电影 | 国产一区二区三区午夜 | 污辱少妇全集观看探花 | 国产丝袜足交视频 | 成人做爱网| 91亚洲成人精品性色 | 欧美亚洲日韩中文字幕 | 吖v在线观看| 插逼逼AV|