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

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

          共 3049字,需瀏覽 7分鐘

           ·

          2021-07-13 22:19

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

          場(chǎng)景

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

          router->controller->service->dao

          目錄大概就長(zhǎng)這樣:

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

          首頁(yè)看main.go文件

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


          dig

          然后我們查看dig.ContainerByDig()

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

          我們看上面的server.NewOrderServer,

          這里的NewOrderServer(xxx)Provide中的語(yǔ)意就是 "我能提供一個(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í)完全沒(méi)有必要,因?yàn)檫@一步dig只會(huì)對(duì)這些函數(shù)進(jìn)行分析,提取函數(shù)的形參以及返回值。然后根據(jù)返回的參數(shù)來(lái)組織容器結(jié)構(gòu)。并不會(huì)在這一步執(zhí)行傳入的函數(shù),所以在Provide階段前后順序并不重要,只要確保不遺漏依賴項(xiàng)即可。

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

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

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

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

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

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

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

          開始執(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è)圖來(lái)描述這個(gè)過(guò)程


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

          這樣會(huì)存在什么問(wèn)題?

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

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

           


          wire

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

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

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

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

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

          你會(huì)發(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ù),然后最終生成我們想要的對(duì)象(服務(wù))。

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

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

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

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


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

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

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

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

          現(xiàn)在我們可以真正使用injector了,我們?cè)谌肟谖募刑鎿Q成dig


          一切正常。

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

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

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

          總結(jié)

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

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

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

          • 依賴關(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)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 191
          點(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>
                  欧美亚洲黄片 | 特级欧美AAAAAA片免费观看 | 午夜高清无码 | 精品视频一区二区三区四区 | 亚洲性爱视频在线观看 |