最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang不到30行代碼實(shí)現(xiàn)依賴注入的方法

 更新時(shí)間:2018年07月27日 08:28:45   作者:xialeistudio  
這篇文章主要介紹了golang不到30行代碼實(shí)現(xiàn)依賴注入的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

本文介紹了golang不到30行代碼實(shí)現(xiàn)依賴注入的方法,分享給大家,具體如下:

項(xiàng)目地址

go-di-demo

本項(xiàng)目依賴

使用標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn),無額外依賴

依賴注入的優(yōu)勢(shì)

用java的人對(duì)于spring框架一定不會(huì)陌生,spring核心就是一個(gè)IoC(控制反轉(zhuǎn)/依賴注入)容器,帶來一個(gè)很大的優(yōu)勢(shì)是解耦。一般只依賴容器,而不依賴具體的類,當(dāng)你的類有修改時(shí),最多需要改動(dòng)一下容器相關(guān)代碼,業(yè)務(wù)代碼并不受影響。

golang的依賴注入原理

總的來說和java的差不多,步驟如下:(golang不支持動(dòng)態(tài)創(chuàng)建對(duì)象,所以需要先手動(dòng)創(chuàng)建對(duì)象然后注入,java可以直接動(dòng)態(tài)創(chuàng)建對(duì)象)

  • 通過反射讀取對(duì)象的依賴(golang是通過tag實(shí)現(xiàn))
  • 在容器中查找有無該對(duì)象實(shí)例
  • 如果有該對(duì)象實(shí)例或者創(chuàng)建對(duì)象的工廠方法,則注入對(duì)象或使用工廠創(chuàng)建對(duì)象并注入
  • 如果無該對(duì)象實(shí)例,則報(bào)錯(cuò)

代碼實(shí)現(xiàn)

一個(gè)典型的容器實(shí)現(xiàn)如下,依賴類型參考了spring的singleton/prototype,分別對(duì)象單例對(duì)象和實(shí)例對(duì)象:

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),
 }
}

// 注冊(cè)單例對(duì)象
func (p *Container) SetSingleton(name string, singleton interface{}) {
 p.Lock()
 p.singletons[name] = singleton
 p.Unlock()
}

// 獲取單例對(duì)象
func (p *Container) GetSingleton(name string) interface{} {
 return p.singletons[name]
}

// 獲取實(shí)例對(duì)象
func (p *Container) GetPrototype(name string) (interface{}, error) {
 factory, ok := p.factories[name]
 if !ok {
  return nil, ErrFactoryNotFound
 }
 return factory()
}

// 設(shè)置實(shí)例對(duì)象工廠
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]
}

// 檢測(cè)是否單例依賴
func (p *Container) isSingleton(tag string) bool {
 tags := strings.Split(tag, ",")
 for _, name := range tags {
  if name == "prototype" {
   return false
  }
 }
 return true
}

// 檢測(cè)是否實(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")
}
  • 最重要的是Ensure方法,該方法掃描實(shí)例的所有export字段,并讀取di標(biāo)簽,如果有該標(biāo)簽則啟動(dòng)注入。
  • 判斷di標(biāo)簽的類型來確定注入singleton或者prototype對(duì)象

測(cè)試

  1. 單例對(duì)象在整個(gè)容器中只有一個(gè)實(shí)例,所以不管在何處注入,獲取到的指針一定是一樣的。
  2. 實(shí)例對(duì)象是通過同一個(gè)工廠方法創(chuàng)建的,所以每個(gè)實(shí)例的指針不可以相同。

下面是測(cè)試入口代碼,完整代碼在github倉(cāng)庫(kù),有興趣的可以翻閱:

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è)對(duì)象之間的實(shí)例化以及依賴關(guān)系,配合配置文件在應(yīng)用初始化階段將需要注入的實(shí)例注冊(cè)到容器中,在應(yīng)用的任何地方只需要在實(shí)例化時(shí)注入容器即可。沒有額外依賴。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Go實(shí)現(xiàn)后臺(tái)任務(wù)調(diào)度系統(tǒng)的實(shí)例代碼

    Go實(shí)現(xiàn)后臺(tái)任務(wù)調(diào)度系統(tǒng)的實(shí)例代碼

    平常我們?cè)陂_發(fā)API的時(shí)候,前端傳遞過來的大批數(shù)據(jù)需要經(jīng)過后端處理,如果后端處理的速度快,前端響應(yīng)就快,反之則很慢,影響用戶體驗(yàn),為了解決這一問題,需要我們自己實(shí)現(xiàn)后臺(tái)任務(wù)調(diào)度系統(tǒng),本文將介紹如何用Go語(yǔ)言實(shí)現(xiàn)后臺(tái)任務(wù)調(diào)度系統(tǒng),需要的朋友可以參考下
    2023-06-06
  • Go語(yǔ)言WaitGroup使用時(shí)需要注意的坑

    Go語(yǔ)言WaitGroup使用時(shí)需要注意的坑

    Go語(yǔ)言中WaitGroup的用途是它能夠一直等到所有的goroutine執(zhí)行完成,并且阻塞主線程的執(zhí)行,直到所有的goroutine執(zhí)行完成。之前一直使用也沒有問題,但最近通過同事的一段代碼引起了關(guān)于WaitGroup的注意,下面這篇文章就介紹了WaitGroup使用時(shí)需要注意的坑及填坑。
    2016-12-12
  • 一文精通管理多版本Go安裝教程

    一文精通管理多版本Go安裝教程

    這篇文章主要為大家介紹了一文精通管理多版本Go安裝教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Go語(yǔ)言tunny的workerWrapper使用教程示例

    Go語(yǔ)言tunny的workerWrapper使用教程示例

    這篇文章主要為大家介紹了Go語(yǔ)言tunny的workerWrapper使用教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • Golang中Error的設(shè)計(jì)與實(shí)踐詳解

    Golang中Error的設(shè)計(jì)與實(shí)踐詳解

    這篇文章主要為大家詳細(xì)介紹了Golang中Error的設(shè)計(jì)以及是具體如何處理錯(cuò)誤的相關(guān)知識(shí),文中的示例代碼簡(jiǎn)潔易懂,需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-08-08
  • golang內(nèi)存對(duì)齊的項(xiàng)目實(shí)踐

    golang內(nèi)存對(duì)齊的項(xiàng)目實(shí)踐

    本文主要介紹了golang內(nèi)存對(duì)齊的項(xiàng)目實(shí)踐,內(nèi)存對(duì)齊不僅有助于提高內(nèi)存訪問效率,還確保了與硬件接口的兼容性,是Go語(yǔ)言編程中不可忽視的重要優(yōu)化手段,下面就來介紹一下
    2025-02-02
  • Go語(yǔ)言實(shí)現(xiàn)Base64、Base58編碼與解碼

    Go語(yǔ)言實(shí)現(xiàn)Base64、Base58編碼與解碼

    本文主要介紹了Base64、Base58編碼與解碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 手把手帶你走進(jìn)Go語(yǔ)言之運(yùn)算符解析

    手把手帶你走進(jìn)Go語(yǔ)言之運(yùn)算符解析

    這篇文章主要介紹了手Go語(yǔ)言之運(yùn)算符解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • go語(yǔ)言中的數(shù)組指針和指針數(shù)組的區(qū)別小結(jié)

    go語(yǔ)言中的數(shù)組指針和指針數(shù)組的區(qū)別小結(jié)

    本文主要介紹了go語(yǔ)言中的數(shù)組指針和指針數(shù)組的區(qū)別小結(jié),文中通過示例代碼介紹的很詳細(xì),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-10-10
  • 在Go中構(gòu)建并發(fā)TCP服務(wù)器

    在Go中構(gòu)建并發(fā)TCP服務(wù)器

    今天小編就為大家分享一篇關(guān)于在Go中構(gòu)建并發(fā)TCP服務(wù)器的文章,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-10-10

最新評(píng)論

巴马| 广水市| 沙坪坝区| 九龙城区| 仲巴县| 安龙县| 云梦县| 博兴县| 满洲里市| 彰化县| 达孜县| 莱芜市| 西华县| 太原市| 桐梓县| 兴和县| 万盛区| 邓州市| 镇沅| 广南县| 凉城县| 英山县| 汽车| 利津县| 莎车县| 湘阴县| 宜君县| 乌兰察布市| 南召县| 子长县| 屏东市| 通城县| 定安县| 祁连县| 鄄城县| 青川县| 赣榆县| 泰顺县| 邹城市| 色达县| 旅游|