日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

淺析golang的依賴注入_Golang

作者:bobo_simpler ? 更新時間: 2022-11-17 編程語言

前言

如果是做web開發,對依賴注入肯定不陌生,java程序員早就習慣了spring提供的依賴注入,做業務開發時非常方便,只關注業務邏輯即可,對象之間的依賴關系都交給框架。

golang是強類型語言,編譯后是機器碼,所以一般使用?反射?或?代碼生成?解決依賴注入的問題

基于反射的DI

基于反射解決DI問題的框架, 使用比較多的是Uber的?dig?庫

官方的例子:

type Config struct {
        Prefix string
}

//初始化Config函數
func NewConfig()(*Config, error) {
        // In a real program, the configuration will probably be read from a
        // file.
        var cfg Config
        err := json.Unmarshal([]byte(`{"prefix": "[foo] "}`), &cfg)
        return &cfg, err
}

//初始化logger函數
func NewLogger(cfg *Config) *log.Logger {
    return log.New(os.Stdout, cfg.Prefix, 0)
}

func Handle() (l *log.Logger) {
    l.Print("You've been invoked")
}

func main() {
    //初始化dig對象
    c := dig.New()

    //Provide方法用來設置依賴的對象
    er := c.Provide(NewConfig)
    if err != nil {
        panic(err)
    }

    //設置依賴的對象
    err = c.Provide(NewLogger)
    if err != nil {
        panic(err)
    }
    
    //執行Handle()方法
    //Handle依賴 Config 和 Logger,使用Invoke執行方法時會自動注入依賴(依賴的對象要傳入Provide方法中)
    err = c.Invoke(Handle)
    if err != nil {
        panic(err)
    }
    // Output:
    // [foo] You've been invoked
}

dig提供了一個容器(container),所有的依賴項通過Provide方法添加,執行某個方法時使用Invoke方法,該方法會自動注入所需要的依賴。

dig使用反射機制解決DI問題,所以代碼執行性能上會有損耗

并且因為使用反射所以可能出現編譯時沒有錯誤,執行時報空指針

詳情使用方法可以參考官方文檔,dig可以繼承到gin框架中,有興趣的可以看看資料。

筆者不太喜歡這種使用方式,為了依賴注入破壞了代碼原有的調用方式。

基于代碼生成的DI

wire庫是google出的解決golang DI問題的工具,它可以 自動生成依賴注入的代碼,節省了手動去處理依賴關系

github地址

wire對原有代碼的侵入度很低,開發過程中,在依賴注入代碼處調用Build方法(例子中是初始化controller對象)就可以了

// +build wireinject

package main

import (
    "encoding/json"
    "fmt"
    "github.com/google/wire"
    "net/http"
)

type DataSource struct {
    Operation string
}

func NewDataSource() DataSource {
    return DataSource{Operation: "operation_name"}
}

//==================

type Dao struct {
    DataSource DataSource
}

func NewDao(ds DataSource) *Dao {
    return &Dao{
        DataSource: ds,
    }
}

func (d *Dao) GetItemList() ([]string, error) {
    //TODO 拿到DB對象做查詢操作
    fmt.Printf("db object: %s", d.DataSource.Operation)
    return []string{d.DataSource.Operation, "item1", "item2"}, nil
}

//====================

type Service struct {
    Dao *Dao
}

func NewService(dao *Dao) *Service {
    return &Service{Dao: dao}
}

func (s *Service) GetItemList() ([]string, error) {
    return s.Dao.GetItemList()
}

//=====================

type Controller struct {
    Service *Service
}

func NewController(service *Service) *Controller {
    return &Controller{Service: service}
}

func (c *Controller) GetItemList() ([]string, error) {
    return c.Service.GetItemList()
}

var MegaSet = wire.NewSet(NewDataSource, NewDao, NewService, NewController)

func initializeController() *Controller {
    wire.Build(MegaSet)
    return &Controller{}
}

func getItemList(w http.ResponseWriter, r *http.Request) {
    controller := initializeController()
    itemList, _ := controller.GetItemList()
    output, _ := json.Marshal(itemList)
    fmt.Fprintf(w, string(output))
}

func main() {
    http.HandleFunc("/items", getItemList)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        panic(err)
    }
}

然后再項目根目錄執行wire命令,會生成構建好依賴關系的代碼(以_gen結尾的文件)

// Code generated by Wire. DO NOT EDIT.

//go:generate go run github.com/google/wire/cmd/wire
//+build !wireinject

package main

import (
    "encoding/json"
    "fmt"
    "github.com/google/wire"
    "net/http"
)

// Injectors from main.go:
// 此處是生成的代碼
func initializeController() *Controller {
    dataSource := NewDataSource()
    dao := NewDao(dataSource)
    service := NewService(dao)
    controller := NewController(service)
    return controller
}

// main.go:

type DataSource struct {
    Operation string
}

func NewDataSource() DataSource {
    return DataSource{Operation: "operation_name"}
}

type Dao struct {
    DataSource DataSource
}

func NewDao(ds DataSource) *Dao {
    return &Dao{
        DataSource: ds,
    }
}

func (d *Dao) GetItemList() ([]string, error) {
    fmt.Printf("db object: %s", d.DataSource.Operation)
    return []string{d.DataSource.Operation, "item1", "item2"}, nil
}

type Service struct {
    Dao *Dao
}

func NewService(dao *Dao) *Service {
    return &Service{Dao: dao}
}

func (s *Service) GetItemList() ([]string, error) {
    return s.Dao.GetItemList()
}

type Controller struct {
    Service *Service
}

func NewController(service *Service) *Controller {
    return &Controller{Service: service}
}

func (c *Controller) GetItemList() ([]string, error) {
    return c.Service.GetItemList()
}

var MegaSet = wire.NewSet(NewDataSource, NewDao, NewService, NewController)

func getItemList(w http.ResponseWriter, r *http.Request) {
    controller := initializeController()
    itemList, _ := controller.GetItemList()
    output, _ := json.Marshal(itemList)
    fmt.Fprintf(w, string(output))
}

func main() {
    http.HandleFunc("/items", getItemList)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        panic(err)
    }
}

關鍵代碼:

//執行wire命令前的代碼
func initializeController() *Controller {
    wire.Build(MegaSet)
    return &Controller{}
}

//執行后生成的代碼
// Injectors from main.go:
func initializeController() *Controller {
    dataSource := NewDataSource()
    dao := NewDao(dataSource)
    service := NewService(dao)
    controller := NewController(service)
    return controller
}

通過生成代碼解決依賴注入的問題,既能提升開發效率,又不影響代碼性能,wire更高級的用法可以去github document查看

  • tips: 如果報錯誤?other declaration of xxxx?,請在源文件頭加上?//+build wireinject
  • go-zero框架也是用wire解決DI問題

原文鏈接:https://blog.csdn.net/bobo_simpler/article/details/125363060

欄目分類
最近更新