golang依赖注入工具wire指南


本文摘自网络,作者,侵删。

wire与依赖注入

Wire 是一个的Golang依赖注入工具,通过自动生成代码的方式在编译期完成依赖注入,Java体系中最出名的Spring框架采用运行时注入,个人认为这是wire和其他依赖注入最大的不同之处。

依赖注入(Dependency Injection)也称作控制反转(Inversion of Control),个人给控制反转下的定义如下:

当前对象需要的依赖对象由外部提供(通常是IoC容器),外部负责依赖对象的构造等操作,当前对象只负责调用,而不关心依赖对象的构造。即依赖对象的控制权交给了IoC容器。

下面给出一个控制反转的示例,比如我们通过配置去创建一个数据库连接:

// 连接配置
type DatabaseConfig struct {
    Dsn string 
}

func NewDB(config *DatabaseConfig)(*sql.DB, error) {
    db,err := sql.Open("mysql", config.Dsn)
    if err != nil {
        return nil, err
    }
    // ...
}

fun NewConfig()(*DatabaseConfig,error) {
    // 读取配置文件
    fp, err := os.Open("config.json")
    if err != nil {
        return nil,err
    }
    defer fp.Close()
    // 解析为Json
    var config DatabaseConfig
    if err:=json.NewDecoder(fp).Decode(&config);err!=nil {
        return nil,err
    }
    return &config, nil
}

func InitDatabase() {
    cfg, err:=NewConfig()
    if err!=nil {
        log.Fatal(err)
    }
    db,err:=NewDB(cfg)
    if err!=nil {
        log.Fatail(err)
    }
    // db对象构造完毕
}

数据库配置怎么来的,NewDB方法并不关心(示例代码采用的是NewConfig提供的JSON配置对象),NewDB只负责创建DB对象并返回,和配置方式并没有耦合,所以即使换成配置中心或者其他方式来提供配置,NewDB代码也无需更改,这就是控制反转的魔力!

来看一个反面例子,也就是控制正转:

当前对象需要的依赖由自己创建,即依赖对象的控制权在当前对象自己手里。
type DatabaseConfig struct {
    Dsn string 
}

func NewDB()(*sql.DB, error) {
    // 读取配置文件
    fp, err := os.Open("config.json")
    if err != nil {
        return nil,err
    }
    defer fp.Close()
    // 解析为Json
    var config DatabaseConfig
    if err:=json.NewDecoder(fp).Decode(&config);err!=nil {
        return nil,err
    }
    // 初始化数据库连接
    db,err = sql.Open("mysql", config.Dsn)
    if err != nil {
        return
    }
    // ...
}

在控制正转模式下,NewDB方法需要自己实现配置对象的创建工作,在示例中需要读取Json配置文件,这是强耦合的代码,一旦配置文件的格式不是Json,NewDB方法将返回错误。

依赖注入固然好用,但是像刚才的例子中去手动管理依赖关系是相当复杂也是相当痛苦的一件事,因此在接下来的内容中会重点介绍golang的依赖注入工具——wire。

上手使用

通过go get github.com/google/wire/cmd/wire安装好wire命令行工具即可。

在正式开始之前需要介绍一下wire中的两个概念:Provider和Injector:

  • Provider:负责创建对象的方法,比如上文中控制反转示例的NewDB(提供DB对象)和NewConfig(提供DatabaseConfig对象)方法。
  • Injector:负责根据对象的依赖,依次构造依赖对象,最终构造目的对象的方法,比如上文中控制反转示例的InitDatabase方法。

现在我们通过wire来实现一个简单的项目。项目结构如下:

|--cmd
    |--main.go
    |--wire.go
|--config
    |--app.json
|--internal
    |--config
        |--config.go
    |--db
        |--db.go

config/app.json

{
  "database": {
    "dsn": "root:root@tcp(localhost:3306)/test"
  }
}

internal/config/config.go

package config

import (
    "encoding/json"
    "github.com/google/wire"
    "os"
)

var Provider = wire.NewSet(New) // 将New方法声明为Provider,表示New方法可以创建一个被别人依赖的对象,也就是Config对象

type Config struct {
    Database database `json:"database"`
}

type database struct {
    Dsn string `json:"dsn"`
}

func New() (*Config, error) {
    fp, err := os.Open("config/app.json")
    if err != nil {
        return nil, err
    }
    defer fp.Close()
    var cfg Config
    if err := json.NewDecoder(fp).Decode(&cfg); err != nil {
        return nil, err
    }
    return &cfg, nil
}

internal/db/db.go

package db

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "github.com/google/wire"
    "wire-example2/internal/config"
)

var Provider = wire.NewSet(New) // 同理

func New(cfg *config.Config) (db *sql.DB, err error) {
    db, err = sql.Open("mysql", cfg.Database.Dsn)
    if err != nil {
        return
    }
    if err = db.Ping(); err != nil {
        return
    }
    return db, nil
}

cmd/main.go

package main

import (
    "database/sql"
    "log"
)

type App struct { // 最终需要的对象
    db *sql.DB
}

func NewApp(db *sql.DB) *App {
    return &App{db: db}
}

func main() {
    app, err := InitApp() // 使用wire生成的injector方法获取app对象
    if err != nil {
        log.Fatal(err)
    }
    var version string
    row := app.db.QueryRow("SELECT VERSION()")
    if err := row.Scan(&version); err != nil {
        log.Fatal(err)
    }
    log.Println(version)
}

cmd/wire.go

阅读剩余部分

相关阅读 >>

Golang 写个归并排序

Go语言循环语句

gtpl Go语言html渲染

Golang test传参数实践

最简单的Go dockerfile编写姿势,没有之一!

Go语言 select 语句

Golang能做高并发的原因

海康/大华sdk协议easycvr如何通过Go语言读取csv文件内容?

Go基础及语法(四)

[译]Go语言最佳实战[一]

更多相关阅读请进入《Go》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...