1.  
  2. 主页
  3.  / 
  4. Go 每日一库
  5.  / 
  6. Go 每日一库之 dig 使用详解

Go 每日一库之 dig 使用详解

简介

今天我们来介绍 Go 语言的一个依赖注入(DI)库——dig

dig 是 uber 开源的库。

Java 依赖注入的库有很多,相信即使不是做 Java 开发的童鞋也听过大名鼎鼎的 Spring。

相比庞大的 Spring,dig 很小巧,实现和使用都比较简洁。

快速使用

第三方库需要先安装,由于我们的示例中使用了前面介绍的go-inigo-flags,这两个库也需要安装:

下面看看如何使用:

在同一目录下创建配置文件my.ini

运行程序,输出:

dig库帮助开发者管理这些对象的创建和维护,每种类型的对象会创建且只创建一次

dig库使用的一般流程:

  • 创建一个容器:dig.New
  • 为想要让dig容器管理的类型创建构造函数,构造函数可以返回多个值,这些值都会被容器管理;
  • 使用这些类型的时候直接编写一个函数,将这些类型作为参数,然后使用container.Invoke执行我们编写的函数。

参数对象

有时候,创建对象有很多依赖,或者编写函数时有多个参数依赖。

如果将这些依赖都作为参数传入,那么代码将变得非常难以阅读:

dig支持将所有参数打包进一个对象中,唯一需要的就是将dig.In内嵌到该类型中:

内嵌了dig.In之后,dig会将该类型中的其它字段看成Object的依赖,创建Object类型的对象时,会先将依赖的Arg1/Arg2/Arg3/Arg4创建好。

上面代码中,类型Config内嵌了dig.InPrintInfo接受一个Config类型的参数。调用Invoke时,dig自动调用InitRedisConfigInitMySQLConfig,并将生成的*RedisConfig*MySQLConfig“打包”成一个Config对象传给PrintInfo

运行结果:

结果对象

前面说过,如果构造函数返回多个值,这些不同类型的值都会存储到dig容器中。参数过多会影响代码的可读性和可维护性,返回值过多同样也是如此。为此,dig提供了返回值对象,返回一个包含多个类型对象的对象。返回的类型,必须内嵌dig.Out

我们把上面的例子稍作修改。将Config内嵌的dig.In变为dig.Out

提供构造函数InitRedisAndMySQLConfig同时创建RedisConfigMySQLConfig,通过Config返回。

这样就不需要将InitRedisConfigInitMySQLConfig加入dig容器了:

PrintInfo直接依赖RedisConfigMySQLConfig

可以看到InitRedisAndMySQLConfig返回Config类型的对象,该类型中的RedisConfigMySQLConfig都被添加到了容器中,PrintInfo函数可直接使用。

运行结果与之前的例子完全一样。

可选依赖

默认情况下,容器如果找不到对应的依赖,那么相应的对象无法创建成功,调用Invoke时也会返回错误。有些依赖不是必须的,dig也提供了一种方式将依赖设置为可选的:

通过在字段后添加结构标签optional:"true",我们将RedisConfig这个依赖设置为可选的,容器中RedisConfig对象也不要紧,这时传入的Configredis为 nil,方法可以正常调用。显然可选依赖只能在参数对象中使用。

我们直接注释掉InitRedisConfig,然后运行程序:

输出:

注意,创建失败和没有提供构造函数是两个概念。

如果InitRedisConfig调用失败了,使用Invoke执行PrintInfo还是会报错的。

命名

前面我们说过,dig默认只会为每种类型创建一个对象。

如果要创建某个类型的多个对象怎么办呢?可以为对象命名!

调用容器的Provide方法时,可以为构造函数的返回对象命名,这样同一个类型就可以有多个对象了。

也可以在结果对象中通过结构标签指定:

然后在参数对象中通过名字指定使用哪个对象:

完整代码:

程序运行结果:

需要注意的时候,NewUser返回的是一个函数,由dig在需要的时候调用。

组可以将相同类型的对象放到一个切片中,可以直接使用这个切片。组的定义与上面名字定义类似。

可以通过为Provide提供额外的参数:

也可以在结果对象中添加结构标签group:"user"

然后我们定义一个参数对象,通过指定同样的结构标签来使用这个切片:

最后我们通过一个完整的例子演示组的使用,我们将创建一个 HTTP 服务器:

我们创建了两个处理器,添加到server组中,在RunServer函数中创建 HTTP 服务器,将这些处理器注册到服务器中。

运行程序,在浏览器中输入localhost:8080/hello1localhost:8080/hello2看看。

常见错误

使用dig过程中会遇到一些错误,我们来看看常见的错误。

Invoke方法在以下几种情况下会返回一个error

  • 无法找到依赖,或依赖创建失败;
  • Invoke执行的函数返回error,该错误也会被传给调用者。

这两种情况,我们都可以判断Invoke的返回值来查找原因。

总结

本文介绍了dig库,它适用于解决循环依赖的对象创建问题。同时也有利于将关注点分离,我们不需要将各种对象传来传去,只需要将构造函数交给dig容器,然后通过Invoke直接使用依赖即可,连判空逻辑都可以省略了!

大家如果发现好玩、好用的 Go 语言库,欢迎到 Go 每日一库 GitHub 上提交 issue。

参考

  1. dig GitHub:https://github.com/uber-go/dig
  2. Go 每日一库 GitHub:https://github.com/darjun/go-daily-lib
这篇文章对您有用吗?

我们要如何帮助您?

发表评论

邮箱地址不会被公开。 必填项已用*标注