91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

golang不到30行代碼實現依賴注入的方法

發布時間:2020-08-29 22:27:51 來源:腳本之家 閱讀:137 作者:xialeistudio 欄目:編程語言

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

項目地址

go-di-demo

本項目依賴

使用標準庫實現,無額外依賴

依賴注入的優勢

用java的人對于spring框架一定不會陌生,spring核心就是一個IoC(控制反轉/依賴注入)容器,帶來一個很大的優勢是解耦。一般只依賴容器,而不依賴具體的類,當你的類有修改時,最多需要改動一下容器相關代碼,業務代碼并不受影響。

golang的依賴注入原理

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

  • 通過反射讀取對象的依賴(golang是通過tag實現)
  • 在容器中查找有無該對象實例
  • 如果有該對象實例或者創建對象的工廠方法,則注入對象或使用工廠創建對象并注入
  • 如果無該對象實例,則報錯

代碼實現

一個典型的容器實現如下,依賴類型參考了spring的singleton/prototype,分別對象單例對象和實例對象:

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
}
// 容器實例化
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]
}

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

// 設置實例對象工廠
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
}

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

// 打印容器內部實例
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方法,該方法掃描實例的所有export字段,并讀取di標簽,如果有該標簽則啟動注入。
  • 判斷di標簽的類型來確定注入singleton或者prototype對象

測試

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

下面是測試入口代碼,完整代碼在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
 }
 // 打印指針,確保單例和實例的指針地址
 fmt.Printf("db: %p\ndb1: %p\nb: %p\nb1: %p\n", a.Db, a.Db1, &a.B, &a.B1)
}

執行之后打印出來的結果為:

db: 0xc4200b6140
db1: 0xc4200b6140
b: 0xc4200a0330
b1: 0xc4200a0338

可以看到兩個db實例的指針一樣,說明是同一個實例,而兩個b的指針不同,說明不是一個實例。

寫在最后

通過依賴注入可以很好的管理多個對象之間的實例化以及依賴關系,配合配置文件在應用初始化階段將需要注入的實例注冊到容器中,在應用的任何地方只需要在實例化時注入容器即可。沒有額外依賴。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

广水市| 屯门区| 内丘县| 三台县| 洛浦县| 丹巴县| 芦山县| 平原县| 孟津县| 曲阳县| 宁明县| 沙河市| 南阳市| 林周县| 余干县| 岳阳县| 定边县| 呼和浩特市| 开平市| 海伦市| 攀枝花市| 左云县| 安仁县| 江川县| 司法| 哈密市| 德令哈市| 西畴县| 齐河县| 临海市| 健康| 新兴县| 长宁区| 滨海县| 祁连县| 西贡区| 渝中区| 遂川县| 和田县| 铁岭市| 兴安县|