go微服務框架kratos學習筆記八 (kratos的依賴注入)

go微服務框架kratos學習筆記八(kratos的依賴注入)


筆記二提過依賴注入,和如何生成,但沒有細講,本文來簡單看看kratos的依賴注入。github

什麼是依賴注入

先來看一個小程序,golang

建立一個小程序模擬迎賓員問候客人的事件小程序

咱們將建立三個結構類型:api

1)爲迎賓員建立消息 message
2)表達消息的迎賓員 greeter
3)迎賓員問候客人的事件 event網絡

type Message string

type Greeter struct {
    // ... TBD
}

type Event struct {
    // ... TBD
}

Message 僅僅攜帶一條string,如今咱們建立一個簡單初始器框架

func NewMessage() Message {
    return Message("Hi there!")
}

咱們的Greeter將須要引用這條消息,讓咱們建立一條初始器給Greeteride

func NewGreeter(m Message) Greeter {
    return Greeter{Message: m}
}

type Greeter struct {
    Message Message // <- adding a Message field
}

在這個初始器中咱們分配了Message 字段給Greeter,如今咱們能用Greeter的方法Greet來獲得一條Message函數

func (g Greeter) Greet() Message {
    return g.Message
}

下一步,咱們一樣也須要一個初始器用Event來建立一個Greeter微服務

func NewEvent(g Greeter) Event {
    return Event{Greeter: g}
}

type Event struct {
    Greeter Greeter // <- adding a Greeter field
}

添加一個Start()來啓動事件

func (e Event) Start() {
    msg := e.Greeter.Greet()
    fmt.Println(msg)
}

Start便是咱們小程序的核心,它告訴greeter去放出一條問候並打印出來。

如今咱們小程序全部的組件就緒了,看看它是如何初始化全部組件的,它看起來多是這樣的

func main() {
    message := NewMessage()
    greeter := NewGreeter(message)
    event := NewEvent(greeter)

    event.Start()
}

首先咱們建立一條message,而後咱們用message建立一個greeter,最後咱們用greeter建立一個event.

這其實就是依賴注入dependency injection簡稱di的原理,

依賴注入基本上就是提供對象須要的對象(其依賴),而不是讓對象本身構造它們。

依賴注入能讓測試變的更爲簡單,咱們能夠經過構造函數來進行注入。

SomeClass() has its constructor as following:

public SomeClass() {
    myObject = Factory.getObject();
}

例如,若是myObject包含複雜的任務像磁盤訪問或者網絡訪問,那麼SomeClass將很難進行單元測試。程序必須模仿myObject而且須要模仿Factory調用

而將myObject做爲參數傳遞給構造函數.

public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

myObject就能直接運行,使測試變的更爲簡單。

能夠經過多種方式將依賴項注入到對象中(例如構造函數注入或setter注入)。甚至可使用專門的依賴項注入框架(例如Spring)來作到這一點,可是確定不是必需的。不須要那些框架的依賴注入。顯式實例化和傳遞對象(依賴項)與框架注入同樣好。

google wire

kratos 使用的 google wire 就是golang的一個依賴注入解決的工具,這個工具可以自動生成類的依賴關係。

依賴注入的一個缺點就是須要如此多的初始化步驟,讓咱們看看如何使用Wire來讓初始化咱們的組件變的更快.

將咱們的小程序main函數改爲以下形式:

func main() {
    e := InitializeEvent()

    e.Start()
}

下一步,分離一個文件wire.go,咱們定義InitializeEvent

// wire.go

func InitializeEvent() Event {
    wire.Build(NewEvent, NewGreeter, NewMessage)
    return Event{}
}

不是依次初始化每一個組件並將其傳遞給下一個組件,而是經過一個wire.Build調用來構建咱們想要的用的初始器。在Wire中,初始化器被稱爲providers,一個提供特定類型的函數。

咱們爲Event添加一個零值做爲返回值,以知足編譯器的要求。
注意,即便咱們向Event添加值,Wire也會忽略它們。
實際上,注入器的目的是提供關於使用哪些providers來構造Event的信息。

InitializeEvent便是一個「注入器」。如今咱們已經完成了注入器

而後在wire.go目錄下運行wire工具。

安裝

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

Wire將找到InitializeEvent注入器並生成一個函數,其主體由全部必要的初始化步驟填充。結果將被寫入名爲wire_gen.go的文件。

// wire_gen.go

func InitializeEvent() Event {
    message := NewMessage()
    greeter := NewGreeter(message)
    event := NewEvent(greeter)
    return event
}

這看上去就像咱們上面手工寫的代碼,想象一下,對於複雜得多的組件,Wire是多麼有用。

kratos中的wire

最後回來看看kratos中的wire.go

// +build wireinject
// The build tag makes sure the stub is not built in the final build.

package di

import (
    pb "demo/api"
    "demo/internal/dao"
    "demo/internal/server/grpc"
    "demo/internal/server/http"
    "demo/internal/service"

    "github.com/google/wire"
)

var daoProvider = wire.NewSet(dao.New, dao.NewDB, dao.NewRedis, dao.NewMC)
var serviceProvider = wire.NewSet(service.New, wire.Bind(new(pb.DemoServer), new(*service.Service)))

func InitApp() (*App, func(), error) {
    panic(wire.Build(daoProvider, serviceProvider, http.New, grpc.New, NewApp))
}

能夠看到kratos用到了wire的一些其它接口:wire.NewSet,wire.Bind,簡單看看。

wire.NewSet

Wire有兩個核心概念:Providersinjectors

Providers

Wire中的主要機制是Providers:一個能夠生成值的函數。這些函數都是普通的Go代碼。

Providers 能夠分組爲provider sets,經過wire.NewSet函數能夠添加一組providers到一個新的集合中。

var daoProvider = wire.NewSet(dao.New, dao.NewDB, dao.NewRedis, dao.NewMC)

固然也能夠添加一個provider sets進一個provider sets

var MegaSet = wire.NewSet(daoProvider, pkg.OtherSet)

injector(注入器)

一個應用程序用injector鏈接這些providers: 一個按依賴順序調用providers的函數,即便用Wire,編寫注入器的簽名,而後Wire生成函數的主體。

調用wire.Build的函數則是來聲明注入器的,返回值不重要,只要類型正確便可。

func InitApp() (*App, func(), error) {
    panic(wire.Build(daoProvider, serviceProvider, http.New, grpc.New, NewApp))
}

Binding Interfaces

最後看看 wire.Bind 用來綁定接口的具體類型。

var serviceProvider = wire.NewSet(service.New, wire.Bind(new(pb.DemoServer), new(*service.Service)))

wire.Bind第一個參數 爲指向所需接口類型的指針,第二個參數爲 指向實現該接口類型的指針,

能夠看到若是不加wire.Bind(new(pb.DemoServer), new(*service.Service)), 能夠看到會找不到依賴。

相關文章
相關標籤/搜索