Go 编译期依赖注入实战:用 google/wire 生成干净的对象装配代码

test12026-10-181 次阅读

为什么需要 Wire

Go 没有运行时反射式容器,手写依赖装配容易出现巨型 main 函数,把 db、cache、repo、service、handler 一层层 new 出来再串起来。google/wire 走的是另一条路:在编译期根据 Provider 声明生成装配函数,生成的代码就是普通 Go 代码,没有运行时反射开销,IDE 也能直接跳转。

定义 Provider 集合

// wire.go
package main

import "github.com/google/wire"

type DB struct{ Dsn string }
type Cache struct{ Addr string }
type Repo struct{ db *DB }
type Service struct{ repo *Repo; cache *Cache }

func NewDB(dsn string) *DB        { return &DB{Dsn: dsn} }
func NewCache(addr string) *Cache { return &Cache{Addr: addr} }
func NewRepo(db *DB) *Repo        { return &Repo{db: db} }
func NewService(repo *Repo, c *Cache) *Service {
    return &Service{repo: repo, cache: c}
}

// ProviderSet 把可复用的装配单元打包
var SuperSet = wire.NewSet(
    NewDB, NewCache, NewRepo, NewService,
)

// InjectService 是 wire 要生成的入口,只声明、不实现
func InjectService(dsn, addr string) *Service {
    wire.Build(SuperSet)
    return nil // 占位,wire 会重写本函数
}

生成与运行

安装 wire 命令行并在项目根执行,它会扫描 Inject 函数、展开 Provider 依赖图,写出 wire_gen.go。

go install github.com/google/wire/cmd/wire@latest
wire gen ./...

生成的 wire_gen.go 大致长这样,纯手工可读、可单步调试:

// wire_gen.go
func InjectService(dsn string, addr string) *Service {
    db := NewDB(dsn)
    cache := NewCache(addr)
    repo := NewRepo(db)
    service := NewService(repo, cache)
    return service
}

落地经验

  • Provider 之间如果出现循环依赖,wire 会在生成期直接报错,比运行时才发现安全得多。
  • 不同环境(test/prod)可以用多套 ProviderSet 切换实现,例如测试用内存 repo 替换真实 DB。
  • wire 只管"怎么 new 和怎么串",不管生命周期;单例靠在 ProviderSet 里复用同一个实例实现。
T

test1

文章作者

为什么需要 Wire Go 没有运行时反射式容器,手写依赖装配容易出现巨型 main 函数,把 db、cache、re...

分类
技术
发布时间
2026-10-18
字数
约 1484 字
阅读
1 次