Golang輕量級(jí)IoC容器安裝使用示例
1. iocgo簡(jiǎn)介
習(xí)慣于Java或者C#開發(fā)的人應(yīng)該對(duì)控制反轉(zhuǎn)與依賴注入應(yīng)該再熟悉不過了。在Java平臺(tái)有鼎鼎大名的Spring框架,在C#平臺(tái)有Autofac,Unity,Windsor等,我當(dāng)年C#開發(fā)時(shí)用的最多的就是Windsor。使用IoC容器是面向?qū)ο箝_發(fā)中非常方便的解耦模塊之間的依賴的方法。各個(gè)模塊之間不依賴于實(shí)現(xiàn),而是依賴于接口,然后在構(gòu)造函數(shù)或者屬性或者方法中注入特定的實(shí)現(xiàn),方便了各個(gè)模塊的拆分以及模塊的獨(dú)立單元測(cè)試。
在[長安鏈]的設(shè)計(jì)中,各個(gè)模塊可以靈活組裝,模塊之間的依賴基于protocol中定義的接口,每個(gè)接口有一個(gè)或者多個(gè)官方實(shí)現(xiàn),當(dāng)然第三方也可以提供該接口更多的實(shí)現(xiàn)。為了實(shí)現(xiàn)更靈活的組裝各個(gè)模塊,管理各個(gè)模塊的依賴關(guān)系,于是我寫了iocgo這個(gè)輕量級(jí)的golang版Ioc容器。
2. iocgo如何使用
2.1 iocgo包的安裝
現(xiàn)在go官方版本已經(jīng)出到1.17了,當(dāng)然我在代碼中其實(shí)也沒有用什么新版本的新特性,于是就用1.15版本或者之后的Go版本即可。要使用iocgo包,直接通過go get添加到項(xiàng)目中:
go get github.com/studyzy/iocgo
2.2 使用示例與說明
2.2.1 最簡(jiǎn)單的例子:
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)建了一個(gè)新的容器,然后在容器中調(diào)用Register方法注冊(cè)了3個(gè)接口和對(duì)應(yīng)的構(gòu)造函數(shù),分別是:
- Foobarer接口對(duì)應(yīng)NewFoobar(f Fooer,b Barer)構(gòu)造函數(shù)
- Fooer接口對(duì)應(yīng)構(gòu)造&Foo{}的匿名函數(shù)。
- Barer接口對(duì)應(yīng)構(gòu)造&Bar{}的匿名函數(shù)。
接下來調(diào)用Resolve函數(shù),并傳入var fb Foobarer 這個(gè)接口變量的指針,iocgo就會(huì)自動(dòng)去構(gòu)建Foobarer對(duì)應(yīng)的實(shí)例,并最終將實(shí)例賦值到fb這個(gè)變量上,于是最后我們就可以正常調(diào)用fb.Say實(shí)例方法了。
2.22. Register 的選項(xiàng)
iocgo的注冊(cè)interface到對(duì)象的函數(shù)定義如下:
func Register(constructor interface{}, options ...Option) error
iocgo為Register函數(shù)提供了以下參數(shù)選項(xiàng)可根據(jù)實(shí)際情況選擇性使用:
- Name 為某個(gè)interface->對(duì)象的映射命名
- Optional 表名這個(gè)構(gòu)造函數(shù)中哪些注入的interface參數(shù)是可選的,如果是可選,那么就算找不到interface對(duì)應(yīng)的實(shí)例也不會(huì)報(bào)錯(cuò)。
- Interface 顯式聲明這個(gè)構(gòu)造函數(shù)返回的實(shí)例是映射到哪個(gè)interface。
- Lifestyle(isTransient) 聲明這個(gè)構(gòu)造函數(shù)在構(gòu)造實(shí)例后是構(gòu)造的臨時(shí)實(shí)例還是單例實(shí)例,如果是臨時(shí)實(shí)例,那么下次再獲取該interface對(duì)應(yīng)的實(shí)例時(shí)需要再次調(diào)用構(gòu)造函數(shù),如果是單例,那么就緩存實(shí)例到容器中,下次再想獲得interface對(duì)應(yīng)的實(shí)例時(shí)直接使用緩存中的,不需要再次構(gòu)造。
- DependsOn 這個(gè)主要是指定構(gòu)造函數(shù)中的某個(gè)參數(shù)在通過容器獲得對(duì)應(yīng)的實(shí)例時(shí),應(yīng)該通過哪個(gè)Name去獲得對(duì)應(yīng)的實(shí)例。
- Parameters 這個(gè)主要用于指定構(gòu)造函數(shù)中的某些非容器托管的參數(shù),比如某構(gòu)造函數(shù)中有int,string等參數(shù),而這些參數(shù)的實(shí)例是不需要通過ioc容器進(jìn)行映射托管的,那么就在這里直接指定。
- Default 這個(gè)主要用于設(shè)置一個(gè)interface對(duì)應(yīng)的默認(rèn)的實(shí)例,也就是如果沒有指定Name的情況下,應(yīng)該找哪個(gè)實(shí)例。 關(guān)于每一個(gè)參數(shù)該如何使用,我都寫了UT樣例,具體參考: container_test.go
2.2.3. 注冊(cè)實(shí)例
如果我們已經(jīng)有了某個(gè)對(duì)象的實(shí)例,那么可以將該實(shí)例和其想映射的interface直接注冊(cè)到ioc容器中,方便其他依賴的對(duì)象獲取,RegisterInstance函數(shù)定義如下:
RegisterInstance(interfacePtr interface{}, instance interface{}, options ...Option) error
使用上也很簡(jiǎn)單,直接將實(shí)例對(duì)應(yīng)的interface的指針作為參數(shù)1,實(shí)例本身作為參數(shù)2,傳入RegisterInstance即可:
b := &Bar{} var bar Barer //interface container.RegisterInstance(&bar, b) // register interface -> instance
2.2.4. 獲得實(shí)例
相關(guān)映射我們通過Register函數(shù)和RegisterInstance函數(shù)已經(jīng)注冊(cè)到容器中,接下來就需要從容器獲得指定的實(shí)例了。獲得實(shí)例需要調(diào)用函數(shù):
func Resolve(abstraction interface{}, options ...ResolveOption) error
這里第一個(gè)參數(shù)abstraction是我們想要獲取的某個(gè)interface的指針,第二個(gè)參數(shù)是可選參數(shù),目前提供的選項(xiàng)有:
- ResolveName 指定使用哪個(gè)name的interface和實(shí)例的映射,如果不指定,那么就是默認(rèn)映射。
- Arguments 指定在調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)獲得實(shí)例時(shí),傳遞的參數(shù),比如int,string等類型的不在ioc容器中托管的參數(shù),可以在這里指定。如果構(gòu)造函數(shù)本身需要這些參數(shù),而且在前面Register的時(shí)候已經(jīng)通過Parameters選項(xiàng)進(jìn)行了指定,那么這里新的指定會(huì)覆蓋原有Register的指定。
var fb Foobarer err:=container.Resolve(&fb)
另外如果我們的構(gòu)造函數(shù)return的值中支持error,而且實(shí)際構(gòu)造的時(shí)候確實(shí)返回了error,那么Resolve函數(shù)也會(huì)返回對(duì)應(yīng)的這個(gè)err。
特別注意:Resolve的第一個(gè)參數(shù)是申明的某個(gè)interface的指針,一定要是指針,不能直接傳interface
2.2.5. 結(jié)構(gòu)體參數(shù)和字段填充
有些時(shí)候構(gòu)造函數(shù)的入?yún)⒎浅6?,于是我們可以申明一個(gè)結(jié)構(gòu)體,把所有入?yún)⒍挤湃脒@個(gè)結(jié)構(gòu)體中,這樣構(gòu)造函數(shù)就只需要一個(gè)參數(shù)了。iocgo也支持自動(dòng)填充這個(gè)結(jié)構(gòu)體中interface對(duì)應(yīng)的實(shí)例,從而構(gòu)造新的對(duì)象。另外iocgo也提供了Fill方法,可以直接填充某個(gè)結(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 //指定這個(gè)字段在獲得對(duì)應(yīng)的實(shí)例時(shí)使用的name
- optional //指定這個(gè)字段是否是可選的,如果是,那么就算獲得不到對(duì)應(yīng)的實(shí)例,也不會(huì)報(bào)錯(cuò)。 示例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ù)注入,我們申明一個(gè)函數(shù),這個(gè)函數(shù)的參數(shù)中有些參數(shù)是interface,那么通過調(diào)用iocgo中的Call方法,可以為這個(gè)函數(shù)注入對(duì)應(yīng)的實(shí)例作為參數(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ù)也是支持選項(xiàng)的,目前提供了2個(gè)選項(xiàng):
- CallArguments 指定函數(shù)中某個(gè)參數(shù)的值
- CallDependsOn 指定函數(shù)中某個(gè)參數(shù)在通過ioc容器獲得實(shí)例時(shí)使用哪個(gè)name來獲得實(shí)例。 最后函數(shù)調(diào)用完成,如果函數(shù)本身有多個(gè)返回值,有error返回,那么Call函數(shù)也會(huì)返回對(duì)應(yīng)的結(jié)果。
2.3 參考:
在寫這個(gè)iocgo的代碼時(shí),主要參考了以下兩個(gè)Ioc相關(guān)的項(xiàng)目:
3. 總結(jié)
iocgo是一個(gè)純Golang語言開發(fā)的用于管理依賴注入的IoC容器,使用這個(gè)容器可以很好的實(shí)現(xiàn)go語言下的面向?qū)ο箝_發(fā),模塊解耦?,F(xiàn)已經(jīng)開源,歡迎大家使用,開源地址:https://github.com/studyzy/iocgo
以上就是Golang輕量級(jí)IoC容器安裝使用示例的詳細(xì)內(nèi)容,更多關(guān)于Golang輕量級(jí)IoC容器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Windows+Linux系統(tǒng)下Go語言環(huán)境安裝配置過程
Go 語言被設(shè)計(jì)成一門應(yīng)用于搭載 Web 服務(wù)器,存儲(chǔ)集群或類似用途的巨型中央服務(wù)器的系統(tǒng)編程語言。這篇文章主要介紹了Windows+Linux系統(tǒng)下Go語言環(huán)境搭建配置過程,針對(duì)每種系統(tǒng)給大家講解的非常詳細(xì),需要的朋友可以參考下2021-06-06golang中接口對(duì)象的轉(zhuǎn)型兩種方式
這篇文章主要介紹了golang中接口對(duì)象的轉(zhuǎn)型方式,大家都知道接口對(duì)象的轉(zhuǎn)型有兩種方式,文中通過示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-10-10gin使用自定義結(jié)構(gòu)綁定表單數(shù)據(jù)的示例代碼
這篇文章主要介紹了gin使用自定義結(jié)構(gòu)綁定表單數(shù)據(jù)的示例代碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11Golang 實(shí)現(xiàn) Redis系列(六)如何實(shí)現(xiàn) pipeline 模式的 redis 客戶端
pipeline 模式的 redis 客戶端需要有兩個(gè)后臺(tái)協(xié)程負(fù)責(zé) tcp 通信,調(diào)用方通過 channel 向后臺(tái)協(xié)程發(fā)送指令,并阻塞等待直到收到響應(yīng),本文是使用 golang 實(shí)現(xiàn) redis 系列的第六篇, 將介紹如何實(shí)現(xiàn)一個(gè) Pipeline 模式的 Redis 客戶端。2021-07-07go語言區(qū)塊鏈學(xué)習(xí)調(diào)用智能合約
這篇文章主要為大家介紹了go語言區(qū)塊鏈學(xué)習(xí)中如何調(diào)用智能合約的實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-10-10