美文网首页
浅谈golang的依赖注入

浅谈golang的依赖注入

作者: 花荣8 | 来源:发表于2022-06-18 21:54 被阅读0次

浅谈golang的依赖注入

如果是做web开发,对依赖注入肯定不陌生,java程序员早就习惯了spring提供的依赖注入,做业务开发时非常方便,只关注业务逻辑即可,对象之间的依赖关系都交给框架。

golang是强类型语言,编译后是机器码,所以一般使用 反射 或 代码生成 解决依赖注入的问题

基于反射的DI

基于反射解决DI问题的框架, 使用比较多的是Uber的 dig 库

github地址:https://github.com/uber-go/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地址:https://github.com/google/wire

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() ([]stringerror) {
    //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() ([]stringerror) {
    return s.Dao.GetItemList()
}

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

type Controller struct {
    Service *Service
}

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

func (c *Controller) GetItemList() ([]stringerror) {
    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)
    }}

gist地址:https://gist.github.com/huarong8/fa9d1f2322fdbd8536402c81437b2567

然后再项目根目录执行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() ([]stringerror) {
    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() ([]stringerror) {
    return s.Dao.GetItemList()
}

type Controller struct {
    Service *Service
}

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

func (c *Controller) GetItemList() ([]stringerror) {
    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问题

相关文章

  • 浅谈golang的依赖注入

    浅谈golang的依赖注入 如果是做web开发,对依赖注入肯定不陌生,java程序员早就习惯了spring提供的依...

  • Golang 反射实现依赖注入

    Golang 反射实现依赖注入 Coding/Golang #Golang #Golang/reflect 依赖注...

  • Dagger2常用注解诠释

    依赖注入 控制反转(IoC)与依赖注入(DI)浅谈依赖注入理解依赖注入(IOC)和学习Unity Gradle配置...

  • 告别Dagger2,Android的Kotlin项目中使用Kod

    前言:依赖注入浅谈 Dagger2的困境 对于依赖注入(Dependency Injection,简称DI)来讲,...

  • golang 依赖注入

    使用facebook的三方库:https://github.com/facebookgo/inject

  • (DI)依赖注入,(IOC)控制反转的理解

    之前面试一些公司会问我对依赖注入、控制反转的理解。这里我就浅谈下自己的理解。 什么是依赖注入,依赖注...

  • 浅谈依赖注入

    依赖注入个人见解 当笔记用,勿喷。 原因 我不想因为我的依赖变化导致每次我都要改造自己 我不想再控制我的依赖了 我...

  • 浅谈依赖注入

    依赖注入是什么? 依赖注入的作用是什么? 依赖注入的应用场景? 如何实现依赖注入? 对于一个后端程序员来说,依赖注...

  • golang wire 依赖注入

    wire执行过程: 一.假设需要定义多个有依赖的启动项(代码如下) 二。新建一个 wire.go 文件,名称不一定...

  • golang反射框架Fx

    一、概述 Fx是一个golang版本的依赖注入框架,它使得golang通过可重用、可组合的模块化来构建golang...

网友评论

      本文标题:浅谈golang的依赖注入

      本文链接:https://www.haomeiwen.com/subject/vaqdvrtx.html