Wire 依赖注入必须显式声明所有构造函数,通过 NewXXX 函数提供依赖,参数只能是已声明的接口类型;需在 wire.go 中用 wire.Build 列出顶层对象及全部 ProviderSet,ProviderSet 必须用 wire.NewSet 聚合,生成的 wire_gen.go 禁止手动修改。

Wire 依赖注入必须显式声明所有构造函数
Wire 不会自动扫描或推断构造逻辑,所有需要注入的类型必须通过 NewXXX 函数显式提供,且参数只能是已声明的类型或其接口。比如你定义了 type UserRepository interface{ ... },那 NewUserService 的签名就必须是 func NewUserService(repo UserRepository) *UserService,不能传 *UserRepoImpl 具体实现——否则 Wire 无法匹配依赖链。
常见错误现象:wire: no provider found for *xxx.UserRepoImpl 或 wire: cannot find provider for xxx.UserRepository,本质都是构造函数签名与依赖需求不一致。
- 确保每个
NewXxx函数返回值类型与它被注入的位置所需类型完全匹配(含指针/值语义) - 接口类型必须在参数位置声明,不能只在返回值里用接口而参数传具体实现
- 避免在构造函数里做副作用操作(如初始化 DB 连接),Wire 只负责组装,不控制调用时机
必须在 wire.go 中定义 ProviderSet 并调用 wire.Build
wire.go 是 Wire 的入口文件,它不参与编译,只供 wire 命令读取并生成 wire_gen.go。这个文件里要定义一个或多个 ProviderSet,再用 wire.Build 显式列出所有需要组装的顶层对象(通常是 *App 或 func() *http.Server 这类启动入口)。
示例片段:
// wire.go
func initApp() *App {
wire.Build(
ServerProviderSet,
RepositoryProviderSet,
ServiceProviderSet,
NewApp,
)
return nil // 这行不会执行,仅占位
}
注意:wire.Build 第一个参数必须是你要最终构建的对象的构造函数;后续参数是它依赖的所有 ProviderSet 或单个 NewXxx 函数。如果漏掉某个中间依赖(比如 NewUserService 没放进任何 ProviderSet 也没直接列在 wire.Build 里),生成就会失败。
ProviderSet 必须用 wire.NewSet 显式聚合
Wire 要求把一组相关构造函数打包成 ProviderSet,不能零散传递。比如数据库相关的一组初始化函数,应统一放入 DatabaseProviderSet:
var DatabaseProviderSet = wire.NewSet( NewDB, NewUserRepo, NewOrderRepo, )
容易踩的坑:
-
wire.NewSet里混入非构造函数(如普通工具函数)会导致生成失败 - 同一个类型被多个
ProviderSet提供(例如两个 set 都有NewDB),Wire 会报冲突,需手动去重 - ProviderSet 名字无特殊含义,但建议按模块命名(如
HTTPProviderSet、GRPCProviderSet),方便排查依赖来源
生成 wire_gen.go 后禁止手动修改
wire_gen.go 是纯生成文件,每次运行 wire 命令都会覆盖。它的内容就是按 wire.Build 描述,把所有 NewXxx 函数按依赖顺序拼成一个初始化函数。一旦你手动改了它,下次生成就丢失改动,而且可能破坏依赖顺序导致 panic。
典型问题场景:
- 想在生成代码里加日志或错误处理?应该改
NewXxx函数本身,而不是wire_gen.go - 生成失败后直接编辑
wire_gen.go绕过?这会让后续依赖变更完全不可控,极易引发 runtime panic - 忘记
go run github.com/google/wire/cmd/wire@latest就跑服务?会 panic:missing dependency,因为wire_gen.go里没生成对应初始化逻辑
真正关键的不是生成动作本身,而是每次新增组件后,你是否重新审视了 wire.Build 的参数列表和各 ProviderSet 的完整性——这才是 Wire 配置最容易被跳过的检查点。


















