Golang輕量級IoC容器安裝使用示例
1. iocgo簡介
習慣于Java或者C#開發(fā)的人應(yīng)該對控制反轉(zhuǎn)與依賴注入應(yīng)該再熟悉不過了。在Java平臺有鼎鼎大名的Spring框架,在C#平臺有Autofac,Unity,Windsor等,我當年C#開發(fā)時用的最多的就是Windsor。使用IoC容器是面向?qū)ο箝_發(fā)中非常方便的解耦模塊之間的依賴的方法。各個模塊之間不依賴于實現(xiàn),而是依賴于接口,然后在構(gòu)造函數(shù)或者屬性或者方法中注入特定的實現(xiàn),方便了各個模塊的拆分以及模塊的獨立單元測試。
在[長安鏈]的設(shè)計中,各個模塊可以靈活組裝,模塊之間的依賴基于protocol中定義的接口,每個接口有一個或者多個官方實現(xiàn),當然第三方也可以提供該接口更多的實現(xiàn)。為了實現(xiàn)更靈活的組裝各個模塊,管理各個模塊的依賴關(guān)系,于是我寫了iocgo這個輕量級的golang版Ioc容器。
2. iocgo如何使用
2.1 iocgo包的安裝
現(xiàn)在go官方版本已經(jīng)出到1.17了,當然我在代碼中其實也沒有用什么新版本的新特性,于是就用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容器進行映射托管的,那么就在這里直接指定。
- Default 這個主要用于設(shè)置一個interface對應(yīng)的默認的實例,也就是如果沒有指定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 -> instance
2.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和實例的映射,如果不指定,那么就是默認映射。
- Arguments 指定在調(diào)用對應(yīng)的構(gòu)造函數(shù)獲得實例時,傳遞的參數(shù),比如int,string等類型的不在ioc容器中托管的參數(shù),可以在這里指定。如果構(gòu)造函數(shù)本身需要這些參數(shù),而且在前面Register的時候已經(jīng)通過Parameters選項進行了指定,那么這里新的指定會覆蓋原有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ā),模塊解耦?,F(xiàn)已經(jīng)開源,歡迎大家使用,開源地址:https://github.com/studyzy/iocgo
以上就是Golang輕量級IoC容器安裝使用示例的詳細內(nèi)容,更多關(guān)于Golang輕量級IoC容器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Windows+Linux系統(tǒng)下Go語言環(huán)境安裝配置過程
Go 語言被設(shè)計成一門應(yīng)用于搭載 Web 服務(wù)器,存儲集群或類似用途的巨型中央服務(wù)器的系統(tǒng)編程語言。這篇文章主要介紹了Windows+Linux系統(tǒng)下Go語言環(huán)境搭建配置過程,針對每種系統(tǒng)給大家講解的非常詳細,需要的朋友可以參考下2021-06-06gin使用自定義結(jié)構(gòu)綁定表單數(shù)據(jù)的示例代碼
這篇文章主要介紹了gin使用自定義結(jié)構(gòu)綁定表單數(shù)據(jù)的示例代碼,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11Golang 實現(xiàn) Redis系列(六)如何實現(xiàn) pipeline 模式的 redis 客戶端
pipeline 模式的 redis 客戶端需要有兩個后臺協(xié)程負責 tcp 通信,調(diào)用方通過 channel 向后臺協(xié)程發(fā)送指令,并阻塞等待直到收到響應(yīng),本文是使用 golang 實現(xiàn) redis 系列的第六篇, 將介紹如何實現(xiàn)一個 Pipeline 模式的 Redis 客戶端。2021-07-07