golang的依赖注入dig

  • 一篇文章带你了解golang依赖注入

dig介绍:

dig 库是一个为 go 提供依赖注入 (dependency injection) 的工具包,基于 reflection 实现的。

在项目中会涉及到很多对象,它们之间的依赖关系可能是这样的

对象 D 的创建依赖于对象 B 和 对象 C

对象 B 和对象 C 的创建依赖于对象 A

func NewD(b *B,c *C) *D{}
func NewB(a *A)*B{}
func NewC(a *A)*C{}
func NewA() *A{}

如果在很多地方都需要用户 D 对象,有两个方法

  1. 从别的地方传一个 D 对象过来
  2. 利用 NewD 重新生成一个新的 D 对象

在 Package 之间进行传递对象,可能会造成 Package 间的耦合,因此一般情况下我们会采取第二种方法。

但现在问题来了,D 对象的生成依赖于 B 对象和 C 对象,要想得到 B 对象和 C 对象,就需要调用 NewB 和 NewC 去生成,而 NewB 和 NewC 需要以 A 为参数,这就需要调用 NewA 来生成 A。项目中不可避免地会出现大量的 NewXXx() 这种方法的调用。dig 库由此而生,按照官方的说法,它就是用来管理这种 对象图 的。


dig基本使用方法:

使用方法总结起来就三步:

  1. 使用 dig.New() 创建 digObj
  2. 调用 digObj.Provide() 提供依赖
  3. 调用 digObj.Invoke() 生成目标
type A struct{}
type B struct{}
type C struct{}
type D struct{}func NewD(b *B, c *C) *D {fmt.Println("NewD()")return new(D)
}
func NewB(a *A) *B {fmt.Println("NewB()")return new(B)
}
func NewC(a *A) *C {fmt.Println("NewC()")return new(C)
}
func NewA() *A {fmt.Println("NewA()")return new(A)
}func main() {// 创建 dig 对象digObj := dig.New()// 利用 Provide 注入依赖digObj.Provide(NewA)digObj.Provide(NewC)digObj.Provide(NewB)digObj.Provide(NewD)var d *DassignD := func(argD *D) {fmt.Println("assignD()")d = argD}fmt.Println("before invoke")// 根据提前注入的依赖来生成对象if err := digObj.Invoke(assignD); err != nil {panic(err)}
}
output:
before invoke
NewA()
NewB()
NewC()
NewD()
assignD()

Privide

func (c *Container) Provide(constructor interface{}, opts ...ProvideOption) error

我们可以把对象的构造想象成一个流水线车间,只要提供了原材料,以及原材料每一步的生成步骤,就可以生成最终的产品。例如在上面的对象图中,原材料就是 A,生成步骤是 NewB,NewC,NewD ,最终得到的产物是 D。

Provide 就是用来提供依赖的,我们利用 Privode 提供生成步骤,Privode 接收一个 constructor ,construct 必须是一个 function obj,不能是一个普通的 Obj,否则 Invoke 时将会失败

func main() {// 创建 dig 对象digObj := dig.New()a := new(A)    // 这里// 利用 Provide 注入依赖digObj.Provide(a)digObj.Provide(NewC)digObj.Provide(NewB)digObj.Provide(NewD)var d *DassignD := func(argD *D) {fmt.Println("assignD()")d = argD}fmt.Println("before invoke")// 根据提前注入的依赖来生成对象if err := digObj.Invoke(assignD); err != nil {// panic: missing type: *main.Apanic(err)}
}

所以你仅仅想通过一个普通的 obj,请用一个 function 把它给包起来,并将这个 function 作为 constructor 传入 Privode() 中。

另一个视角:上面的对象图也可以从另一个角度去理解,原材料是空气,生成步骤是 NewA,NewB,NewC,NewD,其中 NewA 描述了如何利用空气来生成一个 A 对象

**函数式编程的视角:一切对象都是函数,就拿 var a int 中的变量 a 来说,从某种意义上,也可以将它视为一个不接受任何参数并返回 a 本身的一个函数 **

Invoke

func (c *Container) Invoke(function interface{}, opts ...InvokeOption) error

invoke 会利用之前注入的依赖完成对 function 这个参数的调用,参数 function 同样必须是一个 function obj,如果你希望利用 Invoke 来生成一个对象,就像这个对象提前生成,并在 function 中进行赋值。

Invoke 会返回 error,这个 error 是必须要处理的。

在调用 Invoke 方法时,才会真正执行 Provide 中提供的 constructor,且每个 constuctor 只有被调用一次。

type A struct {}func NewA() *A {fmt.Println("NewA()")return new(A)
}func main() {digObj := dig.New()digObj.Provide(NewA)fmt.Println("before invoke")if err := digObj.Invoke(func(*A) {fmt.Println("hello")}); err != nil {panic(err)}if err := digObj.Invoke(func(*A) {fmt.Println("world")}); err != nil {panic(err)}
}
output:
before invoke
NewA()
hello
world

dig.In
有时候某个 struct 可能有很多依赖,这个 struct 的 constructor 可能会过长

func ConstructorA(*B,*C,*C,*E,*F){...}

这时可以将 dig.In 嵌入到 struct 内部,例如

type D struct {dig.In*B*C
}

效果与

// 注意这里返回的是 D 而非 *D

func NewD(b *B, c *C) D {fmt.Println("NewD()")return new(D)
}

相同

type A struct{}
type B struct{}
type C struct{}
type D struct {dig.In*B*C
}
// here!!!
// func NewD(b *B, c *C) *D {//  fmt.Println("NewD()")
//  return new(D)
// }func NewB(a *A) *B {fmt.Println("NewB()")return new(B)
}
func NewC(a *A) *C {fmt.Println("NewC()")return new(C)
}
func NewA() *A {fmt.Println("NewA()")return new(A)
}
func main() {// 创建 dig 对象digObj := dig.New()// 利用 Provide 注入依赖digObj.Provide(NewA)digObj.Provide(NewC)digObj.Provide(NewB)// digObj.Provide(NewD) here!!!var d D    // not a pointer here!!!assignD := func(argD D) { // not a pointer here!!!fmt.Println("assignD()")d = argD}fmt.Println("before invoke")// 根据提前注入的依赖来生成对象if err := digObj.Invoke(assignD); err != nil {// panic missing type: *main.Apanic(err)}
}

dig.Out
dig.Out 和 dig.In 是对称的

dig.In 表示这个 struct 需要哪些依赖
dig.Out 表示这个 struct 可以提供哪些依赖

type BC struct {dig.Out*B*C
}

效果和

// 注意 argument bc 不是指针

func f(bc BC) (*B, *C) {return bc.B, bc.C
}

相同

type A struct{}
type B struct{}
type C struct{}
type D struct {dig.In*B*C
}type BC struct {dig.Out*B*C
}func NewBC(a *A) BC {return BC{}
}func NewA() *A {fmt.Println("NewA()")return new(A)
}
func main() {// 创建 dig 对象digObj := dig.New()// 利用 Provide 注入依赖digObj.Provide(NewA)digObj.Provide(NewBC) // herevar d D               // not a pointer here!!!assignD := func(argD D) { // not a pointer here!!!fmt.Println("assignD()")d = argD}fmt.Println("before invoke")// 根据提前注入的依赖来生成对象if err := digObj.Invoke(assignD); err != nil {// panic missing type: *main.Apanic(err)}
}

一些实验
注意 Provide 中 constructor 和 Invoke 中 function,他两的参数和返回值必须要注意

pointer 和 non-pointer 是不能混用的
interface 和 inplement 也不能混用
case1: Proivde pointer, Invoke non-pointer

func main() {digObj := dig.New()digObj.Provide(func() *A {return new(A)})err := digObj.Invoke(func(A) {fmt.Println("hello")})if err != nil {// panic: missing dependencies for function "main".main.func2 panic(err)}
}

case2: Provide non-pointer, Invoke pointer

func main() {digObj := dig.New()digObj.Provide(func() A {return A{}})err := digObj.Invoke(func(*A) {fmt.Println("hello")})if err != nil {// panic: missing dependencies for function "main".main.func2panic(err)}
}

case3: Provide Implement, Invoke interface

type InterfaceA interface {Hello()
}type ImplementA struct {}func (i ImplementA) Hello() {fmt.Println("hello A")
}func main() {digObj := dig.New()digObj.Provide(func() ImplementA {return ImplementA{}})err := digObj.Invoke(func(a InterfaceA) {a.Hello()})if err != nil {// panic: missing dependencies for function "main".main.func2 panic(err)}
}

golang 依赖注入 dig详解相关推荐

  1. spring依赖注入原理详解(转载)

    spring依赖注入原理详解----转载 所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中.当spring容器启动后,spring容器初始化,创建并管理bean对象,以及销毁它.所 ...

  2. 依赖注入Dagger2详解

    为什么使用依赖注入 首先我们需要知道,人们在很长的一段时间里都是利用控制反转原则规定:应用程序的流程取决于在程序运行时对象图的建立.通过抽象定义的对象交互可以实现这样的动态流程.而使用依赖注入技术或者 ...

  3. Spring 依赖注入方式详解

    平常的Java开发中,程序员在某个类中需要依赖其它类的方法. 通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理. Spring提出了依赖注入的思想,即依赖类不由 ...

  4. Android hilt 依赖注入使用详解

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/128424833 本文出自[赵彦军的博客] 文章目录 官方文档 作用域于树图 添加依 ...

  5. 控制反转(IoC)与依赖注入(DI)详解

    文章目录 什么是控制反转(IoC) 控制反转(IoC)有什么作用 控制反转(IoC)是怎么分类的 依赖注入 接口注入 Setter方法注入 构造器注入 依赖查找 上下文依赖查找(Contextuali ...

  6. spring依赖注入原理详解

    所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中.当spring容器启动后,spring容器初始化,创建并管理bean对象,以及销毁它.所以我们只需从容器直接获取Bean对象就行, ...

  7. Android神匕首—Dagger2依赖注入框架详解

    简介 Dagger-匕首,鼎鼎大名的Square公司旗下又一把利刃(没错!还有一把黄油刀,唤作ButterKnife) Dagger2 是一个Android依赖注入框架,由谷歌开发,最早的版本Dagg ...

  8. spring的依赖注入方式详解

    第一种:使用setter注入 直接使用bean元素的子元素property进行相关的设置 ①简单类型,直接使用value进行赋值. <bean id="somebean" c ...

  9. Spring的注入方式详解

    [html] view plaincopy Spring的注入方式详解 Spring有三个注入方式,type1,type2,type3 type1  接口依赖 type2  setter/getter ...

最新文章

  1. 电压放大倍数公式运放_【专题2:电子工程师 之 硬件】 之 【43.运算放大器详解e 运算放大器虚短和虚断】...
  2. 你不知道的Python的输入输出
  3. Struts2学习---基本配置,action,动态方法调用,action接收参数
  4. 用JavaScript实现一个Timeline
  5. [SQL]UNPIVOT 多個欄位
  6. ES6 类继承 和 super的使用
  7. java 打包工具_Java打包Windows安装程序
  8. Atitit java rest mvc微服务原理以及框架选型 目录 第一节 Mvc原理 model controler view 1 第二章 Spark 最简单 1 第一节 Sprbt to
  9. 一个在线文本比较工具
  10. 几种流行的开源WebService框架Axis1,Axis2,Xfire,CXF,JWS比较
  11. PSP游戏下载地址大全
  12. 在校招中,应届生们如何写出简洁的 Android 开发简历,减少被刷的机率
  13. 西数推出首款SanDisk驱动器:蓝盘和绿盘
  14. 十月下旬腾讯,网易游戏,百度迅雷校园招聘笔试题集锦(第271-330题)
  15. 深度学习基本算法介绍
  16. 超千万人同时在线,抖音快手,是怎么抗住高并发?
  17. 使用office2007打开excel报错:不是有效的win32文件
  18. DCL 对话框 多级窗口开发示例
  19. Anubis的xtr文件简要说明
  20. Origin | origin 2021b 学生免费版 | 中英文切换

热门文章

  1. java 网络流量统计_IP数据包的流量统计(JAVA语言)
  2. [小O地图-数据] - 多条件查询及展示地图POI数据
  3. 密码算法原理与实现:Base64编码
  4. 做人不要太老实-老实人的开心锁(转载)
  5. ssm 框架在插入值的时候总是插不进去,还没有任何错误
  6. 【一起学Java-第二篇】Java语言开发环境
  7. BPG边界网关协议知识(二)
  8. 2017计算机专业职称,2017年计算机职称考试题目及答案
  9. 基于神经网络实现手写数字识别(matlab)
  10. 关于sheetjs家excel工具(js-xlsx)的坑