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)), 能够看到会找不到依赖。

相关文章
相关标签/搜索