本文介紹了golang不到30行代碼實(shí)現(xiàn)依賴注入的方法,分享給大家,具體如下:
網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、小程序設(shè)計(jì)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了海鹽免費(fèi)建站歡迎大家使用!
項(xiàng)目地址
go-di-demo
本項(xiàng)目依賴
使用標(biāo)準(zhǔn)庫實(shí)現(xiàn),無額外依賴
依賴注入的優(yōu)勢
用java的人對于spring框架一定不會(huì)陌生,spring核心就是一個(gè)IoC(控制反轉(zhuǎn)/依賴注入)容器,帶來一個(gè)很大的優(yōu)勢是解耦。一般只依賴容器,而不依賴具體的類,當(dāng)你的類有修改時(shí),最多需要改動(dòng)一下容器相關(guān)代碼,業(yè)務(wù)代碼并不受影響。
golang的依賴注入原理
總的來說和java的差不多,步驟如下:(golang不支持動(dòng)態(tài)創(chuàng)建對象,所以需要先手動(dòng)創(chuàng)建對象然后注入,java可以直接動(dòng)態(tài)創(chuàng)建對象)
代碼實(shí)現(xiàn)
一個(gè)典型的容器實(shí)現(xiàn)如下,依賴類型參考了spring的singleton/prototype,分別對象單例對象和實(shí)例對象:
package di import ( "sync" "reflect" "fmt" "strings" "errors" ) var ( ErrFactoryNotFound = errors.New("factory not found") ) type factory = func() (interface{}, error) // 容器 type Container struct { sync.Mutex singletons map[string]interface{} factories map[string]factory } // 容器實(shí)例化 func NewContainer() *Container { return &Container{ singletons: make(map[string]interface{}), factories: make(map[string]factory), } } // 注冊單例對象 func (p *Container) SetSingleton(name string, singleton interface{}) { p.Lock() p.singletons[name] = singleton p.Unlock() } // 獲取單例對象 func (p *Container) GetSingleton(name string) interface{} { return p.singletons[name] } // 獲取實(shí)例對象 func (p *Container) GetPrototype(name string) (interface{}, error) { factory, ok := p.factories[name] if !ok { return nil, ErrFactoryNotFound } return factory() } // 設(shè)置實(shí)例對象工廠 func (p *Container) SetPrototype(name string, factory factory) { p.Lock() p.factories[name] = factory p.Unlock() } // 注入依賴 func (p *Container) Ensure(instance interface{}) error { elemType := reflect.TypeOf(instance).Elem() ele := reflect.ValueOf(instance).Elem() for i := 0; i < elemType.NumField(); i++ { // 遍歷字段 fieldType := elemType.Field(i) tag := fieldType.Tag.Get("di") // 獲取tag diName := p.injectName(tag) if diName == "" { continue } var ( diInstance interface{} err error ) if p.isSingleton(tag) { diInstance = p.GetSingleton(diName) } if p.isPrototype(tag) { diInstance, err = p.GetPrototype(diName) } if err != nil { return err } if diInstance == nil { return errors.New(diName + " dependency not found") } ele.Field(i).Set(reflect.ValueOf(diInstance)) } return nil } // 獲取需要注入的依賴名稱 func (p *Container) injectName(tag string) string { tags := strings.Split(tag, ",") if len(tags) == 0 { return "" } return tags[0] } // 檢測是否單例依賴 func (p *Container) isSingleton(tag string) bool { tags := strings.Split(tag, ",") for _, name := range tags { if name == "prototype" { return false } } return true } // 檢測是否實(shí)例依賴 func (p *Container) isPrototype(tag string) bool { tags := strings.Split(tag, ",") for _, name := range tags { if name == "prototype" { return true } } return false } // 打印容器內(nèi)部實(shí)例 func (p *Container) String() string { lines := make([]string, 0, len(p.singletons)+len(p.factories)+2) lines = append(lines, "singletons:") for name, item := range p.singletons { line := fmt.Sprintf(" %s: %x %s", name, &item, reflect.TypeOf(item).String()) lines = append(lines, line) } lines = append(lines, "factories:") for name, item := range p.factories { line := fmt.Sprintf(" %s: %x %s", name, &item, reflect.TypeOf(item).String()) lines = append(lines, line) } return strings.Join(lines, "\n") }
測試
下面是測試入口代碼,完整代碼在github倉庫,有興趣的可以翻閱:
package main import ( "di" "database/sql" "fmt" "os" _ "github.com/go-sql-driver/MySQL" "demo" ) func main() { container := di.NewContainer() db, err := sql.Open("mysql", "root:root@tcp(localhost)/sampledb") if err != nil { fmt.Printf("error: %s\n", err.Error()) os.Exit(1) } container.SetSingleton("db", db) container.SetPrototype("b", func() (interface{}, error) { return demo.NewB(), nil }) a := demo.NewA() if err := container.Ensure(a); err != nil { fmt.Println(err) return } // 打印指針,確保單例和實(shí)例的指針地址 fmt.Printf("db: %p\ndb1: %p\nb: %p\nb1: %p\n", a.Db, a.Db1, &a.B, &a.B1) }
執(zhí)行之后打印出來的結(jié)果為:
db: 0xc4200b6140
db1: 0xc4200b6140
b: 0xc4200a0330
b1: 0xc4200a0338
可以看到兩個(gè)db實(shí)例的指針一樣,說明是同一個(gè)實(shí)例,而兩個(gè)b的指針不同,說明不是一個(gè)實(shí)例。
寫在最后
通過依賴注入可以很好的管理多個(gè)對象之間的實(shí)例化以及依賴關(guān)系,配合配置文件在應(yīng)用初始化階段將需要注入的實(shí)例注冊到容器中,在應(yīng)用的任何地方只需要在實(shí)例化時(shí)注入容器即可。沒有額外依賴。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。