Golang輕量級IoC容器安裝使用示例
1. iocgo簡介
習(xí)慣于Java或者C#開發(fā)的人應(yīng)該對控制反轉(zhuǎn)與依賴注入應(yīng)該再熟悉不過了。在Java平臺有鼎鼎大名的Spring框架,在C#平臺有Autofac,Unity,Windsor等,我當(dāng)年C#開發(fā)時用的最多的就是Windsor。使用IoC容器是面向?qū)ο箝_發(fā)中非常方便的解耦模塊之間的依賴的方法。各個模塊之間不依賴于實現(xiàn),而是依賴于接口,然后在構(gòu)造函數(shù)或者屬性或者方法中注入特定的實現(xiàn),方便了各個模塊的拆分以及模塊的獨立單元測試。
在[長安鏈]的設(shè)計中,各個模塊可以靈活組裝,模塊之間的依賴基于protocol中定義的接口,每個接口有一個或者多個官方實現(xiàn),當(dāng)然第三方也可以提供該接口更多的實現(xiàn)。為了實現(xiàn)更靈活的組裝各個模塊,管理各個模塊的依賴關(guān)系,于是我寫了iocgo這個輕量級的golang版Ioc容器。
2. iocgo如何使用
2.1 iocgo包的安裝
現(xiàn)在go官方版本已經(jīng)出到1.17了,當(dāng)然我在代碼中其實也沒有用什么新版本的新特性,于是就用1.15版本或者之后的Go版本即可。要使用iocgo包,直接通過go get添加到項目中:
go get github.com/studyzy/iocgo
2.2 使用示例與說明
2.2.1 最簡單的例子:
type Fooer interface {
Foo(int)
}
type Foo struct {
}
func (Foo)Foo(i int) {
fmt.Println("foo:",i)
}
type Barer interface {
Bar(string)
}
type Bar struct {
}
func (Bar) Bar(s string){
fmt.Println("bar:",s)
}
type Foobarer interface {
Say(int,string)
}
type Foobar struct {
foo Fooer
bar Barer
}
func NewFoobar(f Fooer,b Barer) Foobarer{
return &Foobar{
foo: f,
bar: b,
}
}
func (f Foobar)Say(i int ,s string) {
f.foo.Foo(i)
f.bar.Bar(s)
}
func TestContainer_SimpleRegister(t *testing.T) {
container := NewContainer()
container.Register(NewFoobar)
container.Register(func() Fooer { return &Foo{} })
container.Register(func() Barer { return &Bar{} })
var fb Foobarer
container.Resolve(&fb)
fb.Say(123,"Hello World")
}這里我使用NewContainer()創(chuàng)建了一個新的容器,然后在容器中調(diào)用Register方法注冊了3個接口和對應(yīng)的構(gòu)造函數(shù),分別是:
- Foobarer接口對應(yīng)NewFoobar(f Fooer,b Barer)構(gòu)造函數(shù)
- Fooer接口對應(yīng)構(gòu)造&Foo{}的匿名函數(shù)。
- Barer接口對應(yīng)構(gòu)造&Bar{}的匿名函數(shù)。
接下來調(diào)用Resolve函數(shù),并傳入var fb Foobarer 這個接口變量的指針,iocgo就會自動去構(gòu)建Foobarer對應(yīng)的實例,并最終將實例賦值到fb這個變量上,于是最后我們就可以正常調(diào)用fb.Say實例方法了。
2.22. Register 的選項
iocgo的注冊interface到對象的函數(shù)定義如下:
func Register(constructor interface{}, options ...Option) error
iocgo為Register函數(shù)提供了以下參數(shù)選項可根據(jù)實際情況選擇性使用:
- Name 為某個interface->對象的映射命名
- Optional 表名這個構(gòu)造函數(shù)中哪些注入的interface參數(shù)是可選的,如果是可選,那么就算找不到interface對應(yīng)的實例也不會報錯。
- Interface 顯式聲明這個構(gòu)造函數(shù)返回的實例是映射到哪個interface。
- Lifestyle(isTransient) 聲明這個構(gòu)造函數(shù)在構(gòu)造實例后是構(gòu)造的臨時實例還是單例實例,如果是臨時實例,那么下次再獲取該interface對應(yīng)的實例時需要再次調(diào)用構(gòu)造函數(shù),如果是單例,那么就緩存實例到容器中,下次再想獲得interface對應(yīng)的實例時直接使用緩存中的,不需要再次構(gòu)造。
- DependsOn 這個主要是指定構(gòu)造函數(shù)中的某個參數(shù)在通過容器獲得對應(yīng)的實例時,應(yīng)該通過哪個Name去獲得對應(yīng)的實例。
- Parameters 這個主要用于指定構(gòu)造函數(shù)中的某些非容器托管的參數(shù),比如某構(gòu)造函數(shù)中有int,string等參數(shù),而這些參數(shù)的實例是不需要通過ioc容器進(jìn)行映射托管的,那么就在這里直接指定。
- Default 這個主要用于設(shè)置一個interface對應(yīng)的默認(rèn)的實例,也就是如果沒有指定Name的情況下,應(yīng)該找哪個實例。 關(guān)于每一個參數(shù)該如何使用,我都寫了UT樣例,具體參考: container_test.go
2.2.3. 注冊實例
如果我們已經(jīng)有了某個對象的實例,那么可以將該實例和其想映射的interface直接注冊到ioc容器中,方便其他依賴的對象獲取,RegisterInstance函數(shù)定義如下:
RegisterInstance(interfacePtr interface{}, instance interface{}, options ...Option) error
使用上也很簡單,直接將實例對應(yīng)的interface的指針作為參數(shù)1,實例本身作為參數(shù)2,傳入RegisterInstance即可:
b := &Bar{}
var bar Barer //interface
container.RegisterInstance(&bar, b) // register interface -> instance2.2.4. 獲得實例
相關(guān)映射我們通過Register函數(shù)和RegisterInstance函數(shù)已經(jīng)注冊到容器中,接下來就需要從容器獲得指定的實例了。獲得實例需要調(diào)用函數(shù):
func Resolve(abstraction interface{}, options ...ResolveOption) error
這里第一個參數(shù)abstraction是我們想要獲取的某個interface的指針,第二個參數(shù)是可選參數(shù),目前提供的選項有:
- ResolveName 指定使用哪個name的interface和實例的映射,如果不指定,那么就是默認(rèn)映射。
- Arguments 指定在調(diào)用對應(yīng)的構(gòu)造函數(shù)獲得實例時,傳遞的參數(shù),比如int,string等類型的不在ioc容器中托管的參數(shù),可以在這里指定。如果構(gòu)造函數(shù)本身需要這些參數(shù),而且在前面Register的時候已經(jīng)通過Parameters選項進(jìn)行了指定,那么這里新的指定會覆蓋原有Register的指定。
var fb Foobarer err:=container.Resolve(&fb)
另外如果我們的構(gòu)造函數(shù)return的值中支持error,而且實際構(gòu)造的時候確實返回了error,那么Resolve函數(shù)也會返回對應(yīng)的這個err。
特別注意:Resolve的第一個參數(shù)是申明的某個interface的指針,一定要是指針,不能直接傳interface
2.2.5. 結(jié)構(gòu)體參數(shù)和字段填充
有些時候構(gòu)造函數(shù)的入?yún)⒎浅6?,于是我們可以申明一個結(jié)構(gòu)體,把所有入?yún)⒍挤湃脒@個結(jié)構(gòu)體中,這樣構(gòu)造函數(shù)就只需要一個參數(shù)了。iocgo也支持自動填充這個結(jié)構(gòu)體中interface對應(yīng)的實例,從而構(gòu)造新的對象。另外iocgo也提供了Fill方法,可以直接填充某個結(jié)構(gòu)體,比如:
type FoobarInput struct {
foo Fooer
bar Barer
msg string
}
input := FoobarInput{
msg: "studyzy",
}
container.Register(func() Fooer { return &Foo{} })
container.Register(func() Barer { return &Bar{} })
err := container.Fill(&input)結(jié)構(gòu)體中的字段還支持tag,目前提供的tag有兩種:
- name //指定這個字段在獲得對應(yīng)的實例時使用的name
- optional //指定這個字段是否是可選的,如果是,那么就算獲得不到對應(yīng)的實例,也不會報錯。 示例example:
type FoobarInputWithTag struct {
foo Fooer `optional:"true"`
bar Barer `name:"baz"`
msg string
}2.2.6. 函數(shù)調(diào)用
除了構(gòu)造函數(shù)注入之外,iocgo也支持函數(shù)注入,我們申明一個函數(shù),這個函數(shù)的參數(shù)中有些參數(shù)是interface,那么通過調(diào)用iocgo中的Call方法,可以為這個函數(shù)注入對應(yīng)的實例作為參數(shù),并最終完成函數(shù)的調(diào)用。 示例 example:
func SayHi1(f Fooer, b Barer) {
f.Foo(1234)
b.Bar("hi")
}
Register(func() Fooer { return &Foo{} })
Register(func() Barer { return &Bar{} })
Call(SayHi1)Call函數(shù)也是支持選項的,目前提供了2個選項:
- CallArguments 指定函數(shù)中某個參數(shù)的值
- CallDependsOn 指定函數(shù)中某個參數(shù)在通過ioc容器獲得實例時使用哪個name來獲得實例。 最后函數(shù)調(diào)用完成,如果函數(shù)本身有多個返回值,有error返回,那么Call函數(shù)也會返回對應(yīng)的結(jié)果。
2.3 參考:
在寫這個iocgo的代碼時,主要參考了以下兩個Ioc相關(guān)的項目:
3. 總結(jié)
iocgo是一個純Golang語言開發(fā)的用于管理依賴注入的IoC容器,使用這個容器可以很好的實現(xiàn)go語言下的面向?qū)ο箝_發(fā),模塊解耦。現(xiàn)已經(jīng)開源,歡迎大家使用,開源地址:https://github.com/studyzy/iocgo
以上就是Golang輕量級IoC容器安裝使用示例的詳細(xì)內(nèi)容,更多關(guān)于Golang輕量級IoC容器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go語言處理超大字符串型整數(shù)加減經(jīng)典面試詳解
這篇文章主要為大家介紹了Go語言處理超大字符串型整數(shù)加減經(jīng)典面試示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
并發(fā)安全本地化存儲go-cache讀寫鎖實現(xiàn)多協(xié)程并發(fā)訪問
這篇文章主要介紹了并發(fā)安全本地化存儲go-cache讀寫鎖實現(xiàn)多協(xié)程并發(fā)訪問,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10

