我是靠谱客的博主 名字长了才好记,这篇文章主要介绍go微服务框架kratos学习笔记八(kratos的依赖注入),现在分享给大家,希望可以做个参考。

笔记二提过依赖注入,和如何生成,但没有细讲,本文来简单看看kratos的依赖注入。

什么是依赖注入

先来看一个小程序,

创建一个小程序模拟迎宾员问候客人的事件

我们将创建三个结构类型:

1)为迎宾员创建消息 message
2)表达消息的迎宾员 greeter
3)迎宾员问候客人的事件 event

复制代码
1
2
3
4
5
6
7
8
9
10
type Message string type Greeter struct { // ... TBD } type Event struct { // ... TBD }

Message 仅仅携带一条string,现在我们创建一个简单初始器

复制代码
1
2
3
4
func NewMessage() Message { return Message("Hi there!") }

我们的Greeter将需要引用这条消息,让我们创建一条初始器给Greeter

复制代码
1
2
3
4
5
6
7
8
func NewGreeter(m Message) Greeter { return Greeter{Message: m} } type Greeter struct { Message Message // <- adding a Message field }

在这个初始器中我们分配了Message 字段给Greeter,现在我们能用Greeter的方法Greet来得到一条Message

复制代码
1
2
3
4
func (g Greeter) Greet() Message { return g.Message }

下一步,我们同样也需要一个初始器用Event来创建一个Greeter

复制代码
1
2
3
4
5
6
7
8
func NewEvent(g Greeter) Event { return Event{Greeter: g} } type Event struct { Greeter Greeter // <- adding a Greeter field }

添加一个Start()来启动事件

复制代码
1
2
3
4
5
func (e Event) Start() { msg := e.Greeter.Greet() fmt.Println(msg) }

Start即是我们小程序的核心,它告诉greeter去放出一条问候并打印出来。

现在我们小程序所有的组件就绪了,看看它是如何初始化所有组件的,它看起来可能是这样的

复制代码
1
2
3
4
5
6
7
8
func main() { message := NewMessage() greeter := NewGreeter(message) event := NewEvent(greeter) event.Start() }

首先我们创建一条message,然后我们用message创建一个greeter,最后我们用greeter创建一个event.

这其实就是依赖注入dependency injection简称di的原理,

依赖注入基本上就是提供对象需要的对象(其依赖),而不是让对象自己构造它们。

依赖注入能让测试变的更为简单,我们可以通过构造函数来进行注入。

复制代码
1
2
3
4
5
6
SomeClass() has its constructor as following: public SomeClass() { myObject = Factory.getObject(); }

例如,如果myObject包含复杂的任务像磁盘访问或者网络访问,那么SomeClass将很难进行单元测试。程序必须模仿myObject并且需要模仿Factory调用

而将myObject作为参数传递给构造函数.

复制代码
1
2
3
4
public SomeClass (MyClass myObject) { this.myObject = myObject; }

myObject就能直接运行,使测试变的更为简单。

可以通过多种方式将依赖项注入到对象中(例如构造函数注入或setter注入)。甚至可以使用专门的依赖项注入框架(例如Spring)来做到这一点,但是肯定不是必需的。不需要那些框架的依赖注入。显式实例化和传递对象(依赖项)与框架注入一样好。

google wire

kratos 使用的 google wire 就是golang的一个依赖注入解决的工具,这个工具能够自动生成类的依赖关系。

依赖注入的一个缺点就是需要如此多的初始化步骤,让我们看看如何使用Wire来让初始化我们的组件变的更快.

将我们的小程序main函数改成如下形式:

复制代码
1
2
3
4
5
6
func main() { e := InitializeEvent() e.Start() }

下一步,分离一个文件wire.go,我们定义InitializeEvent

复制代码
1
2
3
4
5
6
7
// 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的文件。

复制代码
1
2
3
4
5
6
7
8
9
// wire_gen.go func InitializeEvent() Event { message := NewMessage() greeter := NewGreeter(message) event := NewEvent(greeter) return event }

这看上去就像我们上面手工写的代码,想象一下,对于复杂得多的组件,Wire是多么有用。

kratos中的wire

最后回来看看kratos中的wire.go

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// +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到一个新的集合中。

复制代码
1
2
var daoProvider = wire.NewSet(dao.New, dao.NewDB, dao.NewRedis, dao.NewMC)

当然也可以添加一个provider sets进一个provider sets

复制代码
1
2
var MegaSet = wire.NewSet(daoProvider, pkg.OtherSet)

injector(注入器)

一个应用程序用injector连接这些providers: 一个按依赖顺序调用providers的函数,即使用Wire,编写注入器的签名,然后Wire生成函数的主体。

调用wire.Build的函数则是来声明注入器的,返回值不重要,只要类型正确即可。

复制代码
1
2
3
4
func InitApp() (*App, func(), error) { panic(wire.Build(daoProvider, serviceProvider, http.New, grpc.New, NewApp)) }

Binding Interfaces

最后看看 wire.Bind 用来绑定接口的具体类型。

复制代码
1
2
3
var serviceProvider = wire.NewSet(service.New, wire.Bind(new(pb.DemoServer), new(*service.Service)))

wire.Bind第一个参数 为指向所需接口类型的指针,第二个参数为 指向实现该接口类型的指针,

可以看到如果不加wire.Bind(new(pb.DemoServer), new(*service.Service)), 可以看到会找不到依赖。

最后

以上就是名字长了才好记最近收集整理的关于go微服务框架kratos学习笔记八(kratos的依赖注入)的全部内容,更多相关go微服务框架kratos学习笔记八(kratos内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(1626)

评论列表共有 0 条评论

立即
投稿
返回
顶部