golang-google-wire

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Persona: You are a Go architect using wire for compile-time DI. You let the compiler catch missing dependencies, treat
wire_gen.go
as committed source, and re-run
wire ./...
after every graph change.
角色定位: 你是一名使用wire实现编译时DI的Go架构师。你会让编译器捕捉缺失的依赖,将
wire_gen.go
作为已提交的源码对待,并且在每次依赖图变更后重新运行
wire ./...

Using google/wire for Compile-Time Dependency Injection in Go

使用google/wire在Go中实现编译时依赖注入

Code-generation DI toolkit. Wire resolves the dependency graph at compile time and emits plain Go constructor calls — no runtime container, no reflection. Errors appear when you run
wire ./...
, not at first request.
Note:
google/wire
was archived in August 2025 (feature-complete; bug fixes still accepted).
This skill is not exhaustive. Please refer to library documentation and code examples for more information. Context7 can help as a discoverability platform.
bash
go install github.com/google/wire/cmd/wire@latest
go get github.com/google/wire
代码生成式DI工具包。Wire在编译时解析依赖图,并生成纯Go构造函数调用——无需运行时容器,无需反射。错误会在你运行
wire ./...
时出现,而非首次请求时。
注意:
google/wire
已于2025年8月归档(功能完善;仍接受bug修复)。
本技能内容并非详尽无遗。如需更多信息,请参考库文档和代码示例。Context7可作为发现平台提供帮助。
bash
go install github.com/google/wire/cmd/wire@latest
go get github.com/google/wire

wire vs. Runtime DI

wire vs. 运行时DI

Concernwiredig / fx / samber/do
ResolutionCompile time (codegen)Runtime (reflection)
Error detection
wire ./...
fails
First
Invoke
/startup
Runtime containerNone — plain Go callsPresent
Lifecycle hooksNot built infx: OnStart/OnStop
Generated files
wire_gen.go
(committed)
None
For lifecycle, lazy loading, and a full matrix see
samber/cc-skills-golang@golang-dependency-injection
.
关注点wiredig / fx / samber/do
依赖解析时机编译时(代码生成)运行时(反射)
错误检测时机运行
wire ./...
时失败
首次
Invoke
/启动时
运行时容器无——纯Go调用存在
生命周期钩子未内置fx: OnStart/OnStop
生成文件
wire_gen.go
(已提交)
如需了解生命周期、延迟加载及完整对比,请查看
samber/cc-skills-golang@golang-dependency-injection

Providers

提供者(Providers)

A provider is any Go function — inputs are dependencies, outputs are provided types. Three return forms:
go
func NewConfig() *Config                          { return &Config{Addr: ":8080"} }
func NewDB(cfg *Config) (*sql.DB, error)          { return sql.Open("postgres", cfg.DSN) }
func NewRedis(cfg *Config) (*redis.Client, func(), error) { // cleanup chained in reverse order
    c := redis.NewClient(&redis.Options{Addr: cfg.RedisAddr})
    return c, func() { c.Close() }, nil
}
提供者是任意Go函数——输入为依赖项,输出为提供的类型。有三种返回形式:
go
func NewConfig() *Config                          { return &Config{Addr: ":8080"} }
func NewDB(cfg *Config) (*sql.DB, error)          { return sql.Open("postgres", cfg.DSN) }
func NewRedis(cfg *Config) (*redis.Client, func(), error) { // 清理函数按逆序执行
    c := redis.NewClient(&redis.Options{Addr: cfg.RedisAddr})
    return c, func() { c.Close() }, nil
}

Provider Sets

提供者集合(Provider Sets)

wire.NewSet
groups providers for reuse. Sets can reference other sets.
go
// infra/wire.go
var InfraSet = wire.NewSet(
    NewConfig,
    NewDB,
    NewRedis,
)

// service/wire.go
var ServiceSet = wire.NewSet(
    NewUserRepo,
    NewUserService,
    wire.Bind(new(UserStore), new(*UserRepo)), // interface binding
)
Keep sets small: library sets expose a stable surface (adding inputs or removing outputs breaks downstream injectors). One set per package is a useful default.
wire.NewSet
将提供者分组以便复用。集合可以引用其他集合。
go
// infra/wire.go
var InfraSet = wire.NewSet(
    NewConfig,
    NewDB,
    NewRedis,
)

// service/wire.go
var ServiceSet = wire.NewSet(
    NewUserRepo,
    NewUserService,
    wire.Bind(new(UserStore), new(*UserRepo)), // 接口绑定
)
保持集合规模较小:库集合应暴露稳定的接口(添加输入或移除输出会破坏下游注入器)。每个包对应一个集合是实用的默认方案。

Injectors and
//go:build wireinject

注入器与
//go:build wireinject

The injector file declares the initialization function. Wire generates its body into
wire_gen.go
and replaces the stub.
go
//go:build wireinject

package main

import "github.com/google/wire"

// Wire generates the body of this function.
func InitApp() (*App, func(), error) {
    wire.Build(InfraSet, ServiceSet, NewApp)
    return nil, nil, nil // replaced by codegen
}
The
//go:build wireinject
tag prevents the stub from being compiled into the binary — only
wire_gen.go
(which has no such tag) makes it through
go build
. Without this tag, both files define the same function, causing a compile error.
Alternative syntax when a dummy return is inconvenient:
go
func InitApp() (*App, func(), error) {
    panic(wire.Build(InfraSet, ServiceSet, NewApp))
}
注入器文件声明初始化函数。Wire会将函数体生成到
wire_gen.go
中并替换存根。
go
//go:build wireinject

package main

import "github.com/google/wire"

// Wire会生成此函数的函数体。
func InitApp() (*App, func(), error) {
    wire.Build(InfraSet, ServiceSet, NewApp)
    return nil, nil, nil // 会被代码生成替换
}
//go:build wireinject
标签可防止存根被编译进二进制文件——只有
wire_gen.go
(无此标签)会通过
go build
编译。如果没有此标签,两个文件会定义相同的函数,导致编译错误。
当虚拟返回值不方便时的替代语法:
go
func InitApp() (*App, func(), error) {
    panic(wire.Build(InfraSet, ServiceSet, NewApp))
}

Interface Bindings

接口绑定

Wire forbids implicit interface satisfaction — you must declare bindings explicitly so the graph is unambiguous when multiple types implement the same interface.
go
var Set = wire.NewSet(
    NewPostgresUserRepo,
    wire.Bind(new(UserStore), new(*PostgresUserRepo)), // tell wire: *PostgresUserRepo satisfies UserStore
)
Explicit bindings prevent graph breakage when a new type implementing the same interface is added elsewhere.
Wire禁止隐式接口实现——你必须显式声明绑定,以便当多个类型实现同一接口时依赖图保持明确。
go
var Set = wire.NewSet(
    NewPostgresUserRepo,
    wire.Bind(new(UserStore), new(*PostgresUserRepo)), // 告知wire:*PostgresUserRepo实现了UserStore
)
显式绑定可防止当其他地方添加了实现同一接口的新类型时依赖图被破坏。

Struct Providers and Values

结构体提供者与值

wire.Struct
fills struct fields from the graph without a manual constructor. Tag fields
wire:"-"
to exclude them.
go
wire.Struct(new(Server), "Logger", "DB") // inject named fields
wire.Struct(new(Server), "*")            // inject all non-excluded fields
wire.Value(Foo{X: 42})                   // constant expression (no fn calls / channels)
wire.InterfaceValue(new(io.Reader), os.Stdin) // interface-typed literal
wire.FieldsOf(new(Config), "DSN", "Addr")    // promote struct fields as graph nodes
See advanced.md for the
wire:"-"
exclusion tag and
wire.FieldsOf
details.
wire.Struct
无需手动构造函数即可从依赖图中填充结构体字段。使用
wire:"-"
标记字段以排除它们。
go
wire.Struct(new(Server), "Logger", "DB") // 注入指定字段
wire.Struct(new(Server), "*")            // 注入所有未排除的字段
wire.Value(Foo{X: 42})                   // 常量表达式(无函数调用/通道)
wire.InterfaceValue(new(io.Reader), os.Stdin) // 接口类型字面量
wire.FieldsOf(new(Config), "DSN", "Addr")    // 将结构体字段提升为依赖图节点
关于
wire:"-"
排除标记和
wire.FieldsOf
的详细信息,请查看advanced.md

Disambiguating Duplicate Types

区分重复类型

Wire forbids two providers for the same type. Wrap the underlying type in distinct named types so each has exactly one provider:
go
type PrimaryDSN string
type ReplicaDSN string
Wire禁止同一类型存在两个提供者。将底层类型包装为不同的命名类型,使每个类型恰好有一个提供者:
go
type PrimaryDSN string
type ReplicaDSN string

Full Application Example

完整应用示例

go
// wire.go — injector, excluded from binary via build tag
//go:build wireinject

package main

func InitApp() (*App, func(), error) {
    wire.Build(config.ConfigSet, infra.InfraSet, service.ServiceSet, NewApp)
    return nil, nil, nil
}

// main.go
func main() {
    app, cleanup, err := InitApp()
    if err != nil { log.Fatal(err) }
    defer cleanup()
    app.Run()
}
Wire generates
wire_gen.go
(plain Go, committed, DO NOT EDIT). For a full example with per-package sets, cleanup-heavy graphs, and generated output, see recipes.md.
go
// wire.go —— 注入器,通过构建标签排除在二进制文件之外
//go:build wireinject

package main

func InitApp() (*App, func(), error) {
    wire.Build(config.ConfigSet, infra.InfraSet, service.ServiceSet, NewApp)
    return nil, nil, nil
}

// main.go
func main() {
    app, cleanup, err := InitApp()
    if err != nil { log.Fatal(err) }
    defer cleanup()
    app.Run()
}
Wire会生成
wire_gen.go
(纯Go代码,已提交,请勿编辑)。如需包含每个包集合、大量清理操作的依赖图及生成输出的完整示例,请查看recipes.md

Codegen Workflow

代码生成工作流

bash
wire ./...           # regenerate all injectors in the module
wire check ./...     # validate graph without regenerating (fast CI check)
Run
wire ./...
after every constructor signature change. Add
//go:generate go run github.com/google/wire/cmd/wire
to injector files so
go generate ./...
also works. Commit
wire_gen.go
— it must stay in sync for CI builds.
bash
wire ./...           # 重新生成模块中的所有注入器
wire check ./...     # 验证依赖图但不重新生成(快速CI检查)
每次构造函数签名变更后运行
wire ./...
。在注入器文件中添加
//go:generate go run github.com/google/wire/cmd/wire
,这样
go generate ./...
也能生效。提交
wire_gen.go
——它必须保持同步以支持CI构建。

Best Practices

最佳实践

  1. Never edit
    wire_gen.go
    — it is overwritten on every
    wire ./...
    run. Treat it as a build artifact that happens to be committed; source of truth is the provider and injector files.
  2. Always add
    //go:build wireinject
    to injector files — omitting it causes duplicate-symbol compile errors because both the stub and the generated file define the same function.
  3. Use named types to distinguish values of the same underlying type — wire enforces one provider per type; named types like
    type DSN string
    let you have
    PrimaryDSN
    and
    ReplicaDSN
    coexist.
  4. Keep library provider sets minimal and backward-compatible — adding new required inputs breaks downstream injectors; removing outputs does too. Introduce only newly-created types in the same release.
  5. Return
    (T, func(), error)
    from cleanup providers and let wire chain them — wire generates the correct reverse-order cleanup and handles partial failures (if construction fails midway, only already-built cleanups run).
  6. Keep injector files focused — one function per file, one package import at a time. Fat injectors with dozens of
    wire.Build
    arguments are hard to reason about; delegate to per-package sets.
  1. 永远不要编辑
    wire_gen.go
    ——每次运行
    wire ./...
    都会覆盖它。将其视为已提交的构建产物;真相源是提供者和注入器文件。
  2. 始终为注入器文件添加
    //go:build wireinject
    ——省略它会导致重复符号编译错误,因为存根和生成文件都定义了相同的函数。
  3. 使用命名类型区分相同底层类型的值——Wire强制每个类型只有一个提供者;像
    type DSN string
    这样的命名类型允许
    PrimaryDSN
    ReplicaDSN
    共存。
  4. 保持库提供者集合最小且向后兼容——添加新的必填输入会破坏下游注入器;移除输出也会如此。仅在同一版本中引入新创建的类型。
  5. 从清理提供者返回
    (T, func(), error)
    并让wire链式处理它们——Wire会生成正确的逆序清理逻辑,并处理部分失败(如果构建中途失败,仅会运行已构建完成的清理函数)。
  6. 保持注入器文件聚焦——每个文件一个函数,每次导入一个包。包含数十个
    wire.Build
    参数的臃肿注入器难以理解;委托给每个包的集合。

Common Mistakes

常见错误

MistakeFix
Editing
wire_gen.go
manually
Never edit it. Change providers or injectors and re-run
wire ./...
.
Missing
//go:build wireinject
Add the tag as the very first line of every injector file.
Two providers returning
*sql.DB
Wrap with named types (
type PrimaryDB *sql.DB
or a wrapper struct).
Injecting an interface without
wire.Bind
Add
wire.Bind(new(MyInterface), new(*MyImpl))
to the provider set.
Forgetting to re-run
wire ./...
after changes
Run wire before
go build
; add it to
go generate
or a Makefile target.
Calling
cleanup()
without guarding for nil
Wire returns nil cleanup on construction error; guard with
if cleanup != nil { defer cleanup() }
.
错误修复方案
手动编辑
wire_gen.go
永远不要编辑它。修改提供者或注入器后重新运行
wire ./...
缺少
//go:build wireinject
在每个注入器文件的第一行添加该标签。
有两个返回
*sql.DB
的提供者
使用命名类型包装(
type PrimaryDB *sql.DB
或包装结构体)。
未使用
wire.Bind
就注入接口
在提供者集合中添加
wire.Bind(new(MyInterface), new(*MyImpl))
变更后忘记重新运行
wire ./...
go build
前运行wire;将其添加到
go generate
或Makefile目标中。
未检查nil就调用
cleanup()
Wire在构建错误时会返回nil清理函数;使用
if cleanup != nil { defer cleanup() }
进行防护。

Testing

测试

Wire generates plain Go constructors, so unit tests use manual injection — no container to clone or reset. For testing patterns (test injectors swapping real providers for fakes, CI stale-check for
wire_gen.go
), see testing.md.
Wire生成纯Go构造函数,因此单元测试使用手动注入——无需克隆或重置容器。如需测试模式(测试注入器将真实提供者替换为模拟实现、CI检查
wire_gen.go
是否过时),请查看testing.md

Further Reading

进一步阅读

  • advanced.md — cleanup chains, multiple injectors, set nesting, error catalogue, codegen flags, quick reference
  • recipes.md — HTTP server, multi-injector build, cleanup-heavy graph, CLI embedding
  • testing.md — test injectors, fake bindings, CI stale check
  • advanced.md —— 清理链、多注入器、集合嵌套、错误目录、代码生成标志、速查手册
  • recipes.md —— HTTP服务器、多注入器构建、大量清理操作的依赖图、CLI嵌入
  • testing.md —— 测试注入器、模拟绑定、CI过时检查

Cross-References

交叉引用

  • → See
    samber/cc-skills-golang@golang-dependency-injection
    skill for DI concepts and library comparison
  • → See
    samber/cc-skills-golang@golang-uber-dig
    skill for runtime reflection-based DI without lifecycle
  • → See
    samber/cc-skills-golang@golang-uber-fx
    skill for runtime DI with lifecycle hooks, modules, and signal-aware Run()
  • → See
    samber/cc-skills-golang@golang-samber-do
    skill for generics-based DI without reflection
  • → See
    samber/cc-skills-golang@golang-structs-interfaces
    skill for interface design patterns
  • → See
    samber/cc-skills-golang@golang-testing
    skill for general testing patterns
If you encounter a bug or unexpected behavior in google/wire, open an issue at https://github.com/google/wire/issues.
  • → 如需了解DI概念及库对比,请查看
    samber/cc-skills-golang@golang-dependency-injection
    技能
  • → 如需了解无生命周期的基于反射的运行时DI,请查看
    samber/cc-skills-golang@golang-uber-dig
    技能
  • → 如需了解带生命周期钩子、模块和信号感知Run()的运行时DI,请查看
    samber/cc-skills-golang@golang-uber-fx
    技能
  • → 如需了解基于泛型的无反射DI,请查看
    samber/cc-skills-golang@golang-samber-do
    技能
  • → 如需了解接口设计模式,请查看
    samber/cc-skills-golang@golang-structs-interfaces
    技能
  • → 如需了解通用测试模式,请查看
    samber/cc-skills-golang@golang-testing
    技能
如果在google/wire中遇到bug或意外行为,请在https://github.com/google/wire/issues提交问题。