我从头到尾实现了一个Golang的依赖注入框架,并且集成了gin、xorm、redis、cron、消息中间件等功能;自己觉得还挺好用的,推荐给你!也欢迎一起维护!
github地址:https://github.com/gone-io/gone
文档地址:https://goner.fun/
如果可能,请帮忙在github上点个 ⭐️ ;万分感谢!!
如果被注入结构体的属性是一个指针,那么这个注入就是 指针注入;值注入 和接口注入 的定义也是类似的。让我们来举个例子:
type AGoner struct {
gone.Flag //tell the framework that this struct is a Goner
Name string
}
func (g *AGoner) Say() {
println("I am the AGoner, My name is", g.Name)
}
type Speaker interface {
Say()
}
type BGoner struct {
gone.Flag //tell the framework that this struct is a Goner
a0 *AGoner `gone:"*"` //匿名注入一个AGoner; 指针注入
a1 *AGoner `gone:"A1"` //具名注入A1; 指针注入
a2 AGoner `gone:"A1"` // 值注入
a3 Speaker `gone:"A2"` // 接口注入
}
上面代码中,BGoner.a0
和 BGoner.a1
是 指针注入;BGoner.a2
是值注入;BGoner.a3
是 接口注入。
需要特别提醒:“在go语言中,值类型 的赋值和传参都是传递的拷贝”,这意味着我们如果使用值注入时,实际上产生了一个新的“对象”,并且新旧对象只有在“传递那一刻”是相等,他们在内存中是独立的;这可能导致一些不符合“直觉”的结果,举个例子:
type BGoner struct {
gone.Flag
a1 AGoner `gone:"A1"` // 值注入
a2 AGoner `gone:"A1"` // 值注入
}
func (g *BGoner) AfterRevive() gone.AfterReviveError {
g.a1.Name = "dapeng"
g.a2.Name = "wang"
fmt.Printf("a1 is eq a2: %v", g.a1 == g.a2)
return nil
}
在上面的代码中,BGoner.a1
和 BGoner.a2
都被注入了 同一个 Goner(A1
),但是因为是值注入,注入的过程中框架实际能做的只有将 A1
Goner 的值拷贝给了 BGoner.a1
和 BGoner.a2
;BGoner.a1
和 BGoner.a2
被注入后,就和A1
没有了任何联系,BGoner.a1
和 BGoner.a2
之间也没有联系,内存中也会有三份AGoner
类型的空间占用;fmt.代码Printf("a1 is eq a2: %v", g.a1 == g.a2)
打印的结果也会是 false
。
考虑到Gone作为基础框架更多意义只是提供可能性,所以我们保留了
值注入
功能;而大多数情况下,我们推荐使用指针注入
和接口注入
。
指针注入 vs 接口注入
在Goner的Bury过程中,要求传递的是一个引用,即Cemetery.Bury
方法的第一个参数必须是引用类型。指针注入和接口注入都可以将 Bury 的引用传递给结构体的属性。指针注入,简单直观,类型间一一对应,没有什么过多需要讲解的。接口(interface)做为go语言中最精华的设计之一,语言设计之初目的就在于 业务使用方和业务实现逻辑的解耦,让接口的使用方不用关注实现的细节。接口的另一个作用是解除循环依赖,如果两个模块间存在循环引用并且他们位于不同的包中;这样会导致package的循环依赖,在go语言中是禁止这样的行为的,编译阶段会失败。我们可以如下使用接口来解出package间的循环依赖。
循环依赖:
使用接口解除循环依赖:
使用接口能够隐藏业务逻辑的实现细节,能够有效的降低模块间的耦合,也更好的遵守“开放封闭”原则;因此,我们推荐使用 接口注入。但是万事没有绝对,引入接口一定会增加额外的成本,所以我们还是支持了 指针注入。
求赞助
如果觉得还可以,请帮忙在github上点个 ⭐️吧:
github地址:https://github.com/gone-io/gone
福利:🔥添加交流群,赠送 Golang 多套 学习资料,夯实基础👍🏻👍🏻