Skip to content

Wire 基本概念 ​

理解 Wire 的核心概念是使用依赖注入的基础。

依赖注入概述 ​

什么是依赖注入 ​

依赖注入(Dependency Injection, DI)是一种设计模式,将依赖关系的创建和管理从代码中分离出来。

go
// 不使用依赖注入
type Service struct {
    db *Database
}

func NewService() *Service {
    db := NewDatabase()  // Service 内部创建 Database
    return &Service{db: db}
}

// 使用依赖注入
func NewService(db *Database) *Service {
    return &Service{db: db}  // Database 由外部传入
}

依赖注入的优势 ​

优势说明
解耦组件不依赖具体实现
可测试易于替换依赖进行测试
可维护依赖关系清晰可见
可扩展易于添加新功能

Wire 解决的问题 ​

go
// 手动组装依赖
func main() {
    db := NewDatabase()
    repo := NewRepository(db)
    service := NewService(repo)
    handler := NewHandler(service)
    
    app := &App{handler: handler}
    app.Run()
}

// Wire 自动组装依赖
func main() {
    app, cleanup, err := InitializeApp()
    if err != nil {
        log.Fatal(err)
    }
    defer cleanup()
    app.Run()
}

Provider ​

Provider 定义 ​

Provider 是创建依赖组件的函数,Wire 使用这些函数构建依赖图。

go
// Provider 函数
func NewDatabase() *Database {
    return &Database{
        Host: "localhost",
        Port: 5432,
    }
}

func NewRepository(db *Database) *Repository {
    return &Repository{db: db}
}

func NewService(repo *Repository) *Service {
    return &Service{repo: repo}
}

Provider 类型 ​

简单 Provider ​

go
func NewLogger() *Logger {
    return &Logger{
        Level: "info",
    }
}

带 Provider Set ​

go
var DatabaseSet = wire.NewSet(
    NewDatabase,
    NewRepository,
)

接口绑定 ​

go
type Repository interface {
    Find(id int) (*User, error)
    Save(user *User) error
}

type MySQLRepository struct {
    db *Database
}

func NewMySQLRepository(db *Database) *MySQLRepository {
    return &MySQLRepository{db: db}
}

// 接口绑定 Provider
var RepositorySet = wire.NewSet(
    NewMySQLRepository,
    wire.Bind(new(Repository), new(*MySQLRepository)),
)

返回错误 ​

go
func NewDatabase(cfg *Config) (*Database, error) {
    if cfg.Host == "" {
        return nil, errors.New("host is required")
    }
    return &Database{Host: cfg.Host}, nil
}

返回清理函数 ​

go
func NewDatabase(cfg *Config) (*Database, func(), error) {
    db, err := sql.Open("mysql", cfg.ConnectionString)
    if err != nil {
        return nil, nil, err
    }
    
    cleanup := func() {
        db.Close()
    }
    
    return &Database{DB: db}, cleanup, nil
}

Injector ​

Injector 定义 ​

Injector 是声明依赖组装方式的函数,Wire 根据 Injector 生成代码。

go
// wire.go
//+build wireinject

package main

import "github.com/google/wire"

func InitializeApp() (*App, error) {
    wire.Build(
        NewConfig,
        NewDatabase,
        NewRepository,
        NewService,
        NewApp,
    )
    return nil, nil
}

Injector 规则 ​

  1. 返回值:Injector 返回组装后的结果
  2. wire.Build:使用 wire.Build 声明所有 Provider
  3. 空返回:Injector 函数体只包含 wire.Build,返回 nil
  4. 构建标签:使用 //+build wireinject 避免编译冲突

Injector 返回类型 ​

go
// 返回单个值
func InitializeApp() *App {
    wire.Build(...)
    return nil
}

// 返回值和错误
func InitializeApp() (*App, error) {
    wire.Build(...)
    return nil, nil
}

// 返回值、清理函数和错误
func InitializeApp() (*App, func(), error) {
    wire.Build(...)
    return nil, nil, nil
}

// 返回多个值
func InitializeApp() (*App, *Service, error) {
    wire.Build(...)
    return nil, nil, nil
}

Provider Set ​

Provider Set 定义 ​

Provider Set 是一组 Provider 的集合,用于组织相关依赖。

go
// 数据层 Provider Set
var DataSet = wire.NewSet(
    NewDatabase,
    NewRepository,
    wire.Bind(new(Repository), new(*MySQLRepository)),
)

// 业务层 Provider Set
var BizSet = wire.NewSet(
    NewService,
    NewUseCase,
)

// 服务层 Provider Set
var ServiceSet = wire.NewSet(
    NewHandler,
    NewServer,
)

组合 Provider Set ​

go
var AllSet = wire.NewSet(
    DataSet,
    BizSet,
    ServiceSet,
    NewApp,
)

func InitializeApp() (*App, error) {
    wire.Build(AllSet)
    return nil, nil
}

依赖图 ​

依赖图概念 ​

Wire 分析 Provider 之间的依赖关系,构建依赖图。

App
  └─ Handler
       └─ Service
            └─ Repository
                 └─ Database
                      └─ Config

依赖图示例 ​

go
// Provider 定义依赖图
func NewConfig() *Config                         // 无依赖
func NewDatabase(cfg *Config) *Database          // 依赖 Config
func NewRepository(db *Database) Repository      // 依赖 Database
func NewService(repo Repository) *Service        // 依赖 Repository
func NewHandler(svc *Service) *Handler           // 依赖 Service
func NewApp(h *Handler) *App                     // 依赖 Handler

// Wire 自动构建依赖图
func InitializeApp() (*App, error) {
    wire.Build(
        NewConfig,
        NewDatabase,
        NewRepository,
        NewService,
        NewHandler,
        NewApp,
    )
    return nil, nil
}

生成代码 ​

go
// wire_gen.go (自动生成)
func InitializeApp() (*App, error) {
    config := NewConfig()
    database, err := NewDatabase(config)
    if err != nil {
        return nil, err
    }
    repository := NewRepository(database)
    service := NewService(repository)
    handler := NewHandler(service)
    app := NewApp(handler)
    return app, nil
}

结构体 Provider ​

结构体作为 Provider ​

go
type App struct {
    Handler *Handler
    Service *Service
}

func NewApp(handler *Handler, service *Service) *App {
    return &App{
        Handler: handler,
        Service: service,
    }
}

// 使用 wire.Struct 自动填充
func InitializeApp() *App {
    wire.Build(
        NewHandler,
        NewService,
        wire.Struct(new(App), "Handler", "Service"),
    )
    return nil
}

wire.Struct 选项 ​

go
// 填充所有字段
wire.Struct(new(App), "*")

// 填充指定字段
wire.Struct(new(App), "Handler", "Service")

// 排除字段
wire.Struct(new(App), "Handler")  // 只填充 Handler

值 Provider ​

使用 wire.Value ​

go
func InitializeApp() *App {
    wire.Build(
        wire.Value(Config{Host: "localhost"}),
        NewDatabase,
        NewApp,
    )
    return nil
}

使用 wire.InterfaceValue ​

go
type Logger interface {
    Info(msg string)
}

func InitializeApp() *App {
    wire.Build(
        wire.InterfaceValue(new(Logger), &DefaultLogger{}),
        NewApp,
    )
    return nil
}

最佳实践 ​

  1. 分层组织:按架构层次组织 Provider Set
  2. 接口绑定:使用接口提高灵活性
  3. 错误处理:Provider 返回错误处理初始化失败
  4. 清理函数:资源需要清理时返回清理函数
  5. 命名清晰:Provider 函数使用 NewXXX 命名
  6. 分离文件:将 wire.go 和业务代码分离

参考:https://github.com/google/wire/blob/main/docs/guide.md