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 次
