Golang 依赖注入库Wire应用案例

简介

Go语言的依赖注入库Wire是由Google提供的一个代码生成工具,用于简化和自动化依赖注入过程。Wire主要通过生成代码来处理依赖关系,而不是在运行时动态地解决依赖,这使得它更高效且易于调试。

  • wire.NewSet: NewSet 函数用于创建一个新的依赖集合。你可以将需要被注入的类型和提供这些类型依赖的函数放入到这个集合中。

  • wire.Build: Build 函数用于生成依赖注入代码。你可以将之前创建的依赖集合传递给 Build 函数,然后它将生成相应的代码用于构建依赖。

  • wire.Bind: Bind 函数用于将一个接口类型与其实现类型进行绑定。这样在依赖注入时,当需要注入某个接口类型时,Wire会知道该使用哪个具体的实现类型。

  • wire.Struct: Struct 函数用于将结构体类型与其字段进行绑定。这样在依赖注入时,可以将结构体的字段通过依赖注入的方式进行初始化。

  • wire.Value: Value 函数用于声明一个特定的值作为依赖。在依赖注入时,这个值会被直接使用,而不需要再进行额外的初始化。

  • wire.InterfaceValue: InterfaceValue 函数用于声明一个接口类型的值作为依赖。在依赖注入时,这个值会被用作接口类型的实现。

  • Wire的应用场景:

    • 构建复杂依赖图:Wire特别适用于处理具有复杂依赖关系的应用程序,尤其是当依赖项数量庞大且层次深厚时。

    • 简化初始化逻辑:通过自动生成依赖注入代码,Wire简化了对象的初始化逻辑,使代码更加简洁和易于维护。

    • 提高代码可测试性:通过接口绑定和依赖注入,Wire可以帮助实现松耦合结构,从而提高代码的可测试性。

    • 提升性能:Wire在编译时解析和生成依赖注入代码,因此在运行时不需要额外的依赖解析步骤,这使得它比一些运行时依赖注入框架更高效。

Github

指南

安装

go get github.com/google/wire/cmd/wire
# 安装
go install github.com/google/wire/cmd/wire@latest

案例

wire.NewSet

wire.NewSet 是 Wire 库中用于创建依赖集合的函数。在依赖注入的场景中,你可以使用 wire.NewSet 将多个提供者函数(即构造函数)或现有值组合到一起,形成一个提供依赖项的集合。这对于组织和管理依赖项非常有帮助。

  • 应用场景
    • 分组管理依赖项:你可以将相关的依赖项分组到一个集合中,方便在不同的构建上下文中复用这些依赖项。
    • 模块化设计:通过创建不同的依赖集合,可以更清晰地表达模块之间的依赖关系。
    • 测试:可以创建不同的依赖集合用于测试,以便轻松替换实际依赖项。

wire.Build

wire.Build 是 Wire 库中的核心函数,用于生成依赖注入代码。它通过构建依赖图,根据提供的依赖集合自动生成所需的初始化代码。

  • 应用场景
    • 复杂依赖关系管理:在大型应用程序中,管理复杂的依赖关系是很困难的,使用 wire.Build 可以自动处理这些依赖。
    • 模块化开发:在模块化开发中,每个模块可能有自己的依赖项,通过 wire.Build 可以轻松组合不同模块的依赖。
    • 测试和替换依赖:在测试中,使用 wire.Build 可以方便地替换实际依赖项以进行单元测试。

在这里插入图片描述

  1. 定义依赖项和构造函数:定义 Database, HTTPServer 和 App 结构体,以及它们的构造函数。
  2. 创建依赖集合:使用 wire.NewSet 将构造函数组合成集合 ProviderSet。
  3. 生成依赖注入代码:在 app/wire.go 中,使用 wire.Build 和 ProviderSet 生成依赖注入代码。
  4. 使用生成的代码:在 main.go 中,调用 app.InitApp 函数初始化应用程序。
cd demo
# 初始化
go mod init example.com/demo
# 下载依赖库
go mod tidy
  • app/app.go
package app

import (
	"github.com/google/wire"
	"example.com/demo/db"
	"example.com/demo/server"
)

// App 是我们的应用程序,依赖于 HTTPServer
type App struct {
	Server *server.HTTPServer
}

// NewApp 是 App 的构造函数
func NewApp(server *server.HTTPServer) *App {
	return &App{
		Server: server,
	}
}

// ProviderSet 是应用程序模块的依赖集合
var ProviderSet = wire.NewSet(NewApp, db.ProviderSet, server.ProviderSet)
  • app/wire.go
// +build wireinject

package app

import (
	"github.com/google/wire"
)

// InitApp 使用 Wire 生成依赖注入代码
func InitApp() *App {
	wire.Build(ProviderSet)
	return &App{}
}
  • app/wire_gen.go(生成的代码)
// Code generated by Wire. DO NOT EDIT.

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

package app

import (
	"example.com/demo/db"
	"example.com/demo/server"
)

// Injectors from wire.go:

// InitApp 使用 Wire 生成依赖注入代码
func InitApp() *App {
	database := db.NewDatabase()
	httpServer := server.NewHTTPServer(database)
	app := NewApp(httpServer)
	return app
}
  • db/db.go
package db

import "github.com/google/wire"

// Database 是一个简单的数据库连接模拟
type Database struct {
	DSN string
}

// NewDatabase 是 Database 的构造函数
func NewDatabase() *Database {
	return &Database{
		DSN: "user:password@/dbname",
	}
}

// ProviderSet 是数据库模块的依赖集合
var ProviderSet = wire.NewSet(NewDatabase)
  • server/server.go
package server

import (
	"example.com/demo/db"
	"github.com/google/wire"
)

// HTTPServer 是一个简单的 HTTP 服务器
type HTTPServer struct {
	DB *db.Database
}

// NewHTTPServer 是 HTTPServer 的构造函数
func NewHTTPServer(db *db.Database) *HTTPServer {
	return &HTTPServer{
		DB: db,
	}
}

// ProviderSet 是服务器模块的依赖集合
var ProviderSet = wire.NewSet(NewHTTPServer)
  • main.go
package main

import (
	"fmt"
	"example.com/demo/app"
)

func main() {
	// 调用 app 包中的 InitApp 函数初始化应用
	myApp := app.InitApp()
	fmt.Printf("App initialized with server: %+v\n", myApp.Server)
}
  • 生成wire_gen.go
cd demo && wire
# 或
cd demo/app && wire
cd demo
go run .

wire.Bind

wire.Bind 是 Wire 提供的一个功能,用于将接口类型与具体实现类型绑定。这在依赖注入的场景中特别有用,因为它允许你将接口注入到依赖树中,而不是具体实现,从而实现更灵活和可测试的代码结构。

  • 应用场景
    • 接口与实现的解耦:当你的代码依赖于接口而不是具体实现时,可以更容易地更换实现或者进行单元测试。
    • 依赖注入中的多态性:在构建依赖图时,可以选择不同的具体实现绑定到相同的接口上,以实现多态行为。

在这里插入图片描述

  1. 定义接口和实现:在 service 包中定义 Service 接口和 RealService 实现。
  2. 构造函数和 ProviderSet:定义 NewRealService 构造函数和 ProviderSet,并使用 wire.Bind 将 Service 接口绑定到 RealService 实现。
  3. 使用依赖注入:在 app 包中定义 App 结构体及其构造函数 NewApp,并在 ProviderSet 中包含 service.ProviderSet。
  4. 生成 Wire 代码:在 app/wire.go 中使用 wire.Build 和 ProviderSet 生成依赖注入代码。
  5. 初始化应用:在 main.go 中调用 app.InitApp 初始化应用,并调用服务的方法。
  • app/app.go
package app

import (
	"github.com/google/wire"
	"example.com/demo/service"
)

// App 是我们的应用程序,依赖于 Service
type App struct {
	Service service.Service
}

// NewApp 是 App 的构造函数
func NewApp(service service.Service) *App {
	return &App{
		Service: service,
	}
}

// ProviderSet 是应用程序模块的依赖集合
var ProviderSet = wire.NewSet(NewApp, service.ProviderSet)
  • app/wire.go
// +build wireinject

package app

import (
	"github.com/google/wire"
)

// InitApp 使用 Wire 生成依赖注入代码
func InitApp() *App {
	wire.Build(ProviderSet)
	return &App{}
}
  • app/wire_gen.go(生成的代码)
// Code generated by Wire. DO NOT EDIT.

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

package app

import (
	"example.com/demo/service"
)

// Injectors from wire.go:

// InitApp 使用 Wire 生成依赖注入代码
func InitApp() *App {
	realService := service.NewRealService()
	app := NewApp(realService)
	return app
}
  • service/service.go
package service

import (
	"github.com/google/wire"
)

// Service 是一个接口,定义了服务的行为
type Service interface {
	Serve() string
}

// RealService 是 Service 接口的具体实现
type RealService struct{}

// Serve 是 RealService 的 Serve 方法实现
func (s *RealService) Serve() string {
	return "Real service serving"
}

// NewRealService 是 RealService 的构造函数
func NewRealService() *RealService {
	return &RealService{}
}

// ProviderSet 是服务模块的依赖集合
var ProviderSet = wire.NewSet(
	NewRealService,
	wire.Bind(new(Service), new(*RealService)),
)
  • main.go
package main

import (
	"fmt"
	"example.com/demo/app"
)

func main() {
	// 调用 app 包中的 InitApp 函数初始化应用
	myApp := app.InitApp()
	fmt.Println(myApp.Service.Serve())
}

wire.Struct

wire.Struct 是 Wire 提供的一个功能,用于简化结构体的构造。当一个结构体有多个字段需要注入时,wire.Struct 可以自动生成构造函数,注入所有字段。它特别适用于需要依赖注入的复杂结构体。

  • 应用场景
    • 简化构造函数:当一个结构体有很多字段时,手动编写构造函数可能会很繁琐,使用 wire.Struct 可以自动生成构造函数。
    • 减少样板代码:在大型项目中,减少手动编写的构造函数代码,提高开发效率。
    • 保证依赖注入的正确性:通过自动生成构造函数,减少人为错误。

在这里插入图片描述

  1. 定义结构体:在 app 包中定义 App 结构体,它依赖于 HTTPServer 和 Database。
  2. 使用 wire.Struct:在 ProviderSet 中使用 wire.Struct(new(App), “*”),表示将自动生成 App 的构造函数并注入所有字段。
  3. 生成依赖注入代码:在 app/wire.go 中使用 wire.Build 和 ProviderSet 生成依赖注入代码。
  4. 初始化应用:在 main.go 中调用 app.InitApp 函数初始化应用。
  • app/app.go
package app

import (
	"github.com/google/wire"
	"example.com/demo/db"
	"example.com/demo/server"
)

// App 是我们的应用程序,依赖于 HTTPServer 和 Database
type App struct {
	Server *server.HTTPServer
	Db     *db.Database
}

// ProviderSet 是应用程序模块的依赖集合
var ProviderSet = wire.NewSet(wire.Struct(new(App), "*"), db.ProviderSet, server.ProviderSet)
  • app/wire.go
// +build wireinject

package app

import (
	"github.com/google/wire"
)

// InitApp 使用 Wire 生成依赖注入代码
func InitApp() *App {
	wire.Build(ProviderSet)
	return &App{}
}
  • app/wire_gen.go(生成的代码)
// Code generated by Wire. DO NOT EDIT.

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

package app

import (
	"example.com/demo/db"
	"example.com/demo/server"
)

// Injectors from wire.go:

// InitApp 使用 Wire 生成依赖注入代码
func InitApp() *App {
	database := db.NewDatabase()
	httpServer := server.NewHTTPServer(database)
	app := &App{
		Server: httpServer,
		Db:     database,
	}
	return app
}
  • db/db.go
package db

import "github.com/google/wire"

// Database 是一个简单的数据库连接模拟
type Database struct {
	DSN string
}

// NewDatabase 是 Database 的构造函数
func NewDatabase() *Database {
	return &Database{
		DSN: "user:password@/dbname",
	}
}

// ProviderSet 是数据库模块的依赖集合
var ProviderSet = wire.NewSet(NewDatabase)
  • server/server.go
package server

import (
	"example.com/demo/db"
	"github.com/google/wire"
)

// HTTPServer 是一个简单的 HTTP 服务器
type HTTPServer struct {
	DB *db.Database
}

// NewHTTPServer 是 HTTPServer 的构造函数
func NewHTTPServer(db *db.Database) *HTTPServer {
	return &HTTPServer{
		DB: db,
	}
}

// ProviderSet 是服务器模块的依赖集合
var ProviderSet = wire.NewSet(NewHTTPServer)
  • main.go
package main

import (
	"fmt"
	"example.com/demo/app"
)

func main() {
	// 调用 app 包中的 InitApp 函数初始化应用
	myApp := app.InitApp()
	fmt.Printf("App initialized with server: %+v\n", myApp.Server)
}

wire.Value

wire.Value 是 Wire 提供的一个功能,用于注入一个特定的值。这在配置项或常量注入等场景中特别有用,能够为依赖注入过程提供静态的值。

  • 应用场景
    • 配置注入:将配置项(如数据库连接字符串、API 密钥等)注入到依赖树中。
    • 静态值注入:对于某些不会改变的常量值,使用 wire.Value 注入这些值。
    • 默认值注入:为某些依赖项提供默认值。

在这里插入图片描述

  1. 定义配置结构体:在 config 包中定义 Config 结构体和 NewConfig 构造函数。
  2. 使用 wire.Value:在 config.ProviderSet 中使用 wire.Value(Config{DSN: “user:password@/dbname”}) 注入静态配置值。
  3. 定义数据库结构体:在 db 包中定义 Database 结构体和 NewDatabase 构造函数。
  4. 构建依赖树:在 app 包中定义 App 结构体及其构造函数 NewApp,并在 ProviderSet 中包含 db.ProviderSet 和 config.ProviderSet。
  5. 生成依赖注入代码:在 app/wire.go 中使用 wire.Build 和 ProviderSet 生成依赖注入代码。
  6. 初始化应用:在 main.go 中调用 app.InitApp 函数初始化应用。
  • app/app.go
package app

import (
	"github.com/google/wire"
	"example.com/demo/db"
)

// App 是我们的应用程序,依赖于 Database
type App struct {
	Database *db.Database
}

// NewApp 是 App 的构造函数
func NewApp(database *db.Database) *App {
	return &App{
		Database: database,
	}
}

// ProviderSet 是应用程序模块的依赖集合
var ProviderSet = wire.NewSet(NewApp, db.ProviderSet)
  • app/wire.go
// +build wireinject

package app

import (
	"github.com/google/wire"
	"example.com/demo/config"
)

// InitApp 使用 Wire 生成依赖注入代码
func InitApp() *App {
	wire.Build(ProviderSet, config.ProviderSet)
	return &App{}
}
  • app/wire_gen.go(生成的代码)
// Code generated by Wire. DO NOT EDIT.

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

package app

import (
	"example.com/demo/config"
	"example.com/demo/db"
)

// Injectors from wire.go:

// InitApp 使用 Wire 生成依赖注入代码
func InitApp() *App {
	config := _wireConfigValue
	database := db.NewDatabase(config)
	app := NewApp(database)
	return app
}

var (
	_wireConfigValue = config.Config{DSN: "user:password@/dbname"}
)
  • config/config.go
package config

import "github.com/google/wire"

// Config 是一个简单的配置结构体
type Config struct {
	DSN string
}

// NewConfig 是 Config 的构造函数
func NewConfig(dsn string) *Config {
	return &Config{
		DSN: dsn,
	}
}

// ProviderSet 是配置模块的依赖集合
var ProviderSet = wire.NewSet(
	wire.Value(Config{DSN: "user:password@/dbname"}),
)
  • db/db.go
package db

import (
	"example.com/demo/config"
	"github.com/google/wire"
)

// Database 是一个简单的数据库连接模拟
type Database struct {
	DSN string
}

// NewDatabase 是 Database 的构造函数
func NewDatabase(config config.Config) *Database {
	return &Database{
		DSN: config.DSN,
	}
}

// ProviderSet 是数据库模块的依赖集合
var ProviderSet = wire.NewSet(NewDatabase)
  • main.go
package main

import (
	"fmt"
	"example.com/demo/app"
)

func main() {
	// 调用 app 包中的 InitApp 函数初始化应用
	myApp := app.InitApp()
	fmt.Printf("App initialized with database DSN: %+v\n", myApp.Database.DSN)
}

wire.InterfaceValue

wire.InterfaceValue 是 Wire 提供的一个功能,用于将一个特定的实现绑定到一个接口。这在依赖注入中非常有用,特别是在你有多个实现并且希望在运行时或编译时决定使用哪个实现的情况下。

  • 应用场景
    • 接口实现的动态选择:当你的代码依赖于接口而不是具体实现时,可以使用 wire.InterfaceValue 动态选择不同的实现。
    • 配置驱动的实现选择:根据配置或环境变量选择不同的实现。
    • 测试替身的注入:在测试中,可以用 wire.InterfaceValue 注入模拟或替身实现。

在这里插入图片描述

  1. 定义接口和实现:在 service 包中定义 Service 接口和 RealService, MockService 实现。
  2. 配置选择实现:在 config 包中定义 SelectedService 函数,用于选择具体的服务实现。
  3. 使用 wire.InterfaceValue:在 app/wire.go 中使用 wire.InterfaceValue 将 config.SelectedService() 的返回值绑定到 Service 接口。
  4. 构建依赖树:在 app 包中定义 App 结构体及其构造函数 NewApp,并在 ProviderSet 中包含 service.ProviderSet。
  5. 生成依赖注入代码:在 app/wire.go 中使用 wire.Build 和 ProviderSet 生成依赖注入代码。
  6. 初始化应用:在 main.go 中调用 app.InitApp 函数初始化应用,并调用服务的方法。
  • app/app.go
package app

import (
	"github.com/google/wire"
	"example.com/demo/service"
)

// App 是我们的应用程序,依赖于 Service
type App struct {
	Service service.Service
}

// NewApp 是 App 的构造函数
func NewApp(service service.Service) *App {
	return &App{
		Service: service,
	}
}

// ProviderSet 是应用程序模块的依赖集合
var ProviderSet = wire.NewSet(NewApp, service.ProviderSet)
  • app/wire.go
// +build wireinject

package app

import (
	"github.com/google/wire"
	"example.com/demo/config"
	"example.com/demo/service"
)

// InitApp 使用 Wire 生成依赖注入代码
func InitApp() *App {
	wire.Build(
		ProviderSet,
		wire.InterfaceValue(new(service.Service), config.SelectedService()),
	)
	return &App{}
}
  • app/wire_gen.go(生成的代码)
// Code generated by Wire. DO NOT EDIT.

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

package app

import (
	"example.com/demo/config"
)

// Injectors from wire.go:

// InitApp 使用 Wire 生成依赖注入代码
func InitApp() *App {
	service := _wireServiceValue
	app := NewApp(service)
	return app
}

var (
	_wireServiceValue = config.SelectedService()
)
  • config/config.go
package config

import "example.com/demo/service"

// SelectedService 返回要使用的服务实现
func SelectedService() service.Service {
	// 根据需要选择实际的服务实现,这里可以是基于配置或环境变量的逻辑
	return &service.RealService{}
}
  • db/db.go
package db

import (
	"example.com/demo/config"
	"github.com/google/wire"
)

// Database 是一个简单的数据库连接模拟
type Database struct {
	DSN string
}

// NewDatabase 是 Database 的构造函数
func NewDatabase(config config.Config) *Database {
	return &Database{
		DSN: config.DSN,
	}
}

// ProviderSet 是数据库模块的依赖集合
var ProviderSet = wire.NewSet(NewDatabase)
  • service/service.go
package service

import "github.com/google/wire"

// Service 是一个接口,定义了服务的行为
type Service interface {
	Serve() string
}

// RealService 是 Service 接口的一个具体实现
type RealService struct{}

func (s *RealService) Serve() string {
	return "Real service serving"
}

// MockService 是 Service 接口的另一个具体实现
type MockService struct{}

func (s *MockService) Serve() string {
	return "Mock service serving"
}

// ProviderSet 是服务模块的依赖集合
var ProviderSet = wire.NewSet()
  • main.go
package main

import (
	"fmt"
	"example.com/demo/app"
)

func main() {
	// 调用 app 包中的 InitApp 函数初始化应用
	myApp := app.InitApp()
	fmt.Println(myApp.Service.Serve())
}

相关推荐

  1. golang wire 依赖注入

    2024-06-18 23:58:02       11 阅读
  2. Golang:依赖注入wire

    2024-06-18 23:58:02       6 阅读
  3. go依赖注入samber/do使用

    2024-06-18 23:58:02       36 阅读
  4. Golang-web开发好用的依赖

    2024-06-18 23:58:02       19 阅读
  5. 安卓Dagger框架:依赖注入实践与应用

    2024-06-18 23:58:02       14 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-18 23:58:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-18 23:58:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-18 23:58:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-18 23:58:02       18 阅读

热门阅读

  1. Spring Cloud Gateway 概述与基本配置(上)

    2024-06-18 23:58:02       6 阅读
  2. 从零学习es8

    2024-06-18 23:58:02       5 阅读
  3. Stage模型

    2024-06-18 23:58:02       6 阅读
  4. 正规式理解

    2024-06-18 23:58:02       5 阅读
  5. 一文看懂E2PROM、FLASH等芯片,软件开发

    2024-06-18 23:58:02       6 阅读
  6. Vue3源码【三】—— createApp执行流程源码分析

    2024-06-18 23:58:02       4 阅读