golang-uber-dig

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Persona: You are a Go architect wiring an application graph with dig. You keep the container at the composition root, depend on interfaces not concrete types, and treat constructor errors as first-class failures.
角色定位: 你是一名使用dig构建应用依赖图的Go架构师。你将容器置于组合根目录,依赖接口而非具体类型,并将构造函数错误视为首要故障。

Using uber-go/dig for Dependency Injection in Go

使用uber-go/dig实现Go语言中的依赖注入

Reflection-based DI toolkit, designed to power application frameworks (it is the engine behind
uber-go/fx
) and resolve object graphs during startup.
Official Resources:
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 get go.uber.org/dig
When to choose dig over fx. Use raw dig only when you need the wiring graph and not fx's lifecycle, signal handling, or app boot semantics. For most production apps, prefer fx (
samber/cc-skills-golang@golang-uber-fx
skill) — it adds lifecycle hooks, modules, and signal-aware
Run()
on top of the same dig engine.
基于反射的DI工具包,旨在为应用框架提供支持(它是
uber-go/fx
的底层引擎),并在启动时解析对象依赖图。
官方资源:
本技能内容并非详尽无遗。如需更多信息,请参考库文档和代码示例。Context7可作为发现平台提供帮助。
bash
go get go.uber.org/dig
何时选择dig而非fx:仅当你需要依赖注入图,而不需要fx的生命周期、信号处理或应用启动语义时,才使用原生dig。对于大多数生产应用,优先选择fx(
samber/cc-skills-golang@golang-uber-fx
技能)——它在同一dig引擎之上添加了生命周期钩子、模块以及支持信号处理的
Run()
方法。

Container

容器

go
import "go.uber.org/dig"

c := dig.New()
Useful options:
dig.DeferAcyclicVerification()
(faster startup),
dig.RecoverFromPanics()
(turn panics into
dig.PanicError
),
dig.DryRun(true)
(validate without invoking).
go
import "go.uber.org/dig"

c := dig.New()
实用选项:
dig.DeferAcyclicVerification()
(启动更快)、
dig.RecoverFromPanics()
(将恐慌转换为
dig.PanicError
)、
dig.DryRun(true)
(仅验证不执行)。

Provide and Invoke

Provide与Invoke

go
// Register a constructor — lazy, only runs when its output is needed
err := c.Provide(func(cfg *Config) (*sql.DB, error) {
    return sql.Open("postgres", cfg.DSN)
})

// Pull a service out of the container by asking for it as a function parameter
err = c.Invoke(func(db *sql.DB) error {
    return db.Ping()
})
Constructors are lazy and memoized: each output type is built once and shared (singleton per container).
Provide
errors at registration if the constructor is malformed;
Invoke
returns the constructor's error wrapped with the dependency path that triggered it.
A dig constructor is any function. Inputs are dependencies, outputs are provided types.
error
(last return) signals construction failure. Follow "accept interfaces, return structs".
go
// 注册构造函数——懒加载,仅在需要其输出时执行
err := c.Provide(func(cfg *Config) (*sql.DB, error) {
    return sql.Open("postgres", cfg.DSN)
})

// 通过将服务作为函数参数传入,从容器中获取服务
err = c.Invoke(func(db *sql.DB) error {
    return db.Ping()
})
构造函数是懒加载记忆化的:每种输出类型仅构建一次并共享(每个容器对应一个单例)。如果构造函数格式错误,
Provide
会在注册时返回错误;
Invoke
会返回构造函数的错误,并附上触发该错误的依赖路径。
任何函数都可以作为dig构造函数。输入为依赖项,输出为提供的类型。最后一个返回值
error
表示构造失败。遵循“依赖接口,返回结构体”的原则。

Parameter Objects with
dig.In

使用
dig.In
的参数对象

Once a constructor has 4+ dependencies, embed
dig.In
to group them as struct fields and tag fields:
go
type HandlerParams struct {
    dig.In

    Logger *zap.Logger
    DB     *sql.DB
    Cache  *redis.Client `optional:"true"`           // zero value if not provided
    DBRO   *sql.DB       `name:"readonly"`           // named dependency
    Routes []http.Handler `group:"routes"`           // value group
}

func NewHandler(p HandlerParams) *Handler { /* ... */ }
Tags:
name:"..."
,
optional:"true"
,
group:"..."
.
当构造函数有4个及以上依赖项时,嵌入
dig.In
将它们分组为结构体字段并添加标签:
go
type HandlerParams struct {
    dig.In

    Logger *zap.Logger
    DB     *sql.DB
    Cache  *redis.Client `optional:"true"`           // 若未提供则为零值
    DBRO   *sql.DB       `name:"readonly"`           // 命名依赖
    Routes []http.Handler `group:"routes"`           // 值组
}

func NewHandler(p HandlerParams) *Handler { /* ... */ }
标签:
name:"..."
optional:"true"
group:"..."

Result Objects with
dig.Out

使用
dig.Out
的结果对象

Return several values from one constructor and attach
name
/
group
tags to results:
go
type ConnResult struct {
    dig.Out

    ReadWrite *sql.DB `name:"primary"`
    ReadOnly  *sql.DB `name:"readonly"`
}

func NewConnections(cfg *Config) (ConnResult, error) { /* ... */ }
从一个构造函数返回多个值,并为结果附加
name
/
group
标签:
go
type ConnResult struct {
    dig.Out

    ReadWrite *sql.DB `name:"primary"`
    ReadOnly  *sql.DB `name:"readonly"`
}

func NewConnections(cfg *Config) (ConnResult, error) { /* ... */ }

Named Values

命名值

Two providers of the same type collide. Disambiguate with
dig.Name
:
go
c.Provide(NewPrimaryDB,  dig.Name("primary"))
c.Provide(NewReadOnlyDB, dig.Name("readonly"))
Consume by adding
name:"primary"
/
name:"readonly"
to a
dig.In
field.
同一类型的两个提供者会产生冲突。使用
dig.Name
来区分:
go
c.Provide(NewPrimaryDB,  dig.Name("primary"))
c.Provide(NewReadOnlyDB, dig.Name("readonly"))
通过在
dig.In
字段上添加
name:"primary"
/
name:"readonly"
来使用命名值。

Value Groups

值组

Many providers, one consumer slice — typical for HTTP handlers, health checks, migrations:
go
type RouteResult struct {
    dig.Out
    Handler http.Handler `group:"routes"`
}

func NewUserHandler(db *sql.DB) RouteResult { /* ... */ }
func NewPostHandler(db *sql.DB) RouteResult { /* ... */ }

type ServerParams struct {
    dig.In
    Routes []http.Handler `group:"routes"`
}
Flatten — append
,flatten
(e.g.
group:"routes,flatten"
) to unwrap a slice instead of nesting it. Group order is not guaranteed; if order matters, provide an explicit ordered slice from a single constructor.
多个提供者对应一个消费者切片——典型场景如HTTP处理器、健康检查、迁移:
go
type RouteResult struct {
    dig.Out
    Handler http.Handler `group:"routes"`
}

func NewUserHandler(db *sql.DB) RouteResult { /* ... */ }
func NewPostHandler(db *sql.DB) RouteResult { /* ... */ }

type ServerParams struct {
    dig.In
    Routes []http.Handler `group:"routes"`
}
扁平化——添加
,flatten
(例如
group:"routes,flatten"
)来展开切片而非嵌套。值组的顺序不保证;如果顺序重要,请通过单个构造函数提供显式排序的切片。

Provide as Interface (
dig.As
)

以接口形式提供(
dig.As

Register a concrete constructor and expose it under one or more interfaces without a separate adapter:
go
c.Provide(NewPostgresDB, dig.As(new(Database), new(io.Closer)))
// Consumers ask for Database or io.Closer; *PostgresDB stays hidden.
注册一个具体构造函数,并在不使用单独适配器的情况下将其暴露为一个或多个接口:
go
c.Provide(NewPostgresDB, dig.As(new(Database), new(io.Closer)))
// 消费者请求Database或io.Closer;*PostgresDB保持隐藏。

Full Application Example

完整应用示例

go
func main() {
    c := dig.New()

    must(c.Provide(NewConfig))
    must(c.Provide(NewLogger))
    must(c.Provide(NewDatabase))
    must(c.Provide(NewServer))

    err := c.Invoke(func(srv *http.Server) error {
        return srv.ListenAndServe()
    })
    if err != nil {
        log.Fatal(err)
    }
}

func must(err error) { if err != nil { panic(err) } }
dig has no built-in lifecycle. If you need OnStart/OnStop hooks, signal handling, and graceful shutdown, use fx — see
samber/cc-skills-golang@golang-uber-fx
skill.
For Decorate, Scopes, optional deps, error helpers, and Visualize, see advanced.md.
go
func main() {
    c := dig.New()

    must(c.Provide(NewConfig))
    must(c.Provide(NewLogger))
    must(c.Provide(NewDatabase))
    must(c.Provide(NewServer))

    err := c.Invoke(func(srv *http.Server) error {
        return srv.ListenAndServe()
    })
    if err != nil {
        log.Fatal(err)
    }
}

func must(err error) { if err != nil { panic(err) } }
dig没有内置生命周期。如果需要OnStart/OnStop钩子、信号处理和优雅关闭,请使用fx——查看
samber/cc-skills-golang@golang-uber-fx
技能。
如需了解Decorate、作用域、可选依赖、错误助手和可视化功能,请查看advanced.md

Best Practices

最佳实践

  1. Keep the container at the composition root — never pass
    *dig.Container
    as a parameter; treat it like a plumbing detail of
    main()
    . Service-locator patterns defeat the testability gains of DI.
  2. Depend on interfaces, not concrete types — lets you swap implementations in tests without touching production code, and lets you use
    dig.As
    to expose narrow interfaces from wide structs.
  3. Prefer parameter objects (
    dig.In
    structs) once a constructor has 4+ dependencies — call sites stay readable and adding a new dependency is a one-line change instead of a signature break.
  4. Group registration by module (one file per module that calls
    c.Provide
    for its types) — review and refactoring become a per-module concern, and you can extract a module into a fx.Module later without rewriting wiring.
  5. Validate the graph eagerly in tests — call
    c.Invoke
    against the composition root in CI to surface missing providers at boot time, not at first request.
    DryRun(true)
    skips constructor execution.
  6. Return errors from constructors instead of panicking — dig wraps them with the dependency path, which makes the failure point obvious.
  1. 将容器置于组合根目录——切勿将
    *dig.Container
    作为参数传递;将其视为
    main()
    的底层实现细节。服务定位器模式会抵消DI带来的可测试性优势。
  2. 依赖接口而非具体类型——这让你无需修改生产代码即可在测试中替换实现,还能使用
    dig.As
    从宽泛的结构体中暴露窄接口。
  3. 当构造函数有4个及以上依赖项时,优先使用参数对象(
    dig.In
    结构体)——调用点保持可读性,添加新依赖只需一行修改,无需更改函数签名。
  4. 按模块分组注册(每个模块对应一个调用
    c.Provide
    注册其类型的文件)——代码审查和重构可按模块进行,之后无需重写依赖注入代码即可将模块提取为fx.Module。
  5. 在测试中提前验证依赖图——在CI中针对组合根目录调用
    c.Invoke
    ,在启动时发现缺失的提供者,而非在首次请求时。
    DryRun(true)
    会跳过构造函数执行。
  6. 从构造函数返回错误而非恐慌——dig会将错误与依赖路径一起包装,让故障点一目了然。

Common Mistakes

常见错误

MistakeFix
Passing the container into servicesThe container belongs to
main()
. Inject the typed dependencies a service needs; otherwise tests need to build a real container.
Two providers for the same type without
Name
dig errors at
Provide
time. Either name them, or merge into a single provider that returns a
dig.Out
result struct.
Ignoring
Provide
errors
Wrap each
Provide
with a
must
helper. A silent registration error becomes a missing-type error far later.
Using groups when ordering mattersGroups are unordered. If order matters (middleware chain, migration sequence), provide an explicit ordered slice with one constructor.
Constructors with side effects on importKeep
init()
empty — start work only inside the constructor, after the graph is built.
错误修复方案
将容器传入服务中容器属于
main()
。注入服务所需的类型化依赖;否则测试需要构建真实容器。
同一类型的两个提供者未使用
Name
dig会在
Provide
时报错。要么为它们命名,要么合并为一个返回
dig.Out
结果结构体的提供者。
忽略
Provide
错误
must
助手包装每个
Provide
。静默的注册错误会在很久之后变成缺失类型错误。
在顺序重要的场景使用值组值组是无序的。如果顺序重要(中间件链、迁移序列),请通过单个构造函数提供显式排序的切片。
导入时构造函数产生副作用保持
init()
为空——仅在构造函数内部、依赖图构建完成后启动工作。

Testing

测试

dig containers are cheap — build a fresh one per test, override providers with
Decorate
, and call
Invoke
to drive the system. For full patterns (per-test wiring, shared helpers, graph validation in CI, asserting wire-time errors, recovering from constructor panics), see testing.md.
dig容器成本低廉——为每个测试构建一个新容器,使用
Decorate
覆盖提供者,调用
Invoke
驱动系统。如需完整模式(每个测试的依赖注入、共享助手、CI中的依赖图验证、断言注入时的错误、从构造函数恐慌中恢复),请查看testing.md

Further Reading

延伸阅读

  • advanced.md — Decorate, Scopes, optional deps, error helpers, Visualize, full Quick Reference
  • recipes.md — end-to-end examples: HTTP server with route group, two databases, request scopes, decorators, dry-run validation
  • testing.md — testing patterns and graph validation
  • advanced.md —— Decorate、作用域、可选依赖、错误助手、可视化、完整快速参考
  • recipes.md —— 端到端示例:带路由组的HTTP服务器、双数据库、请求作用域、装饰器、 dry-run验证
  • testing.md —— 测试模式与依赖图验证

Cross-References

交叉参考

  • → See
    samber/cc-skills-golang@golang-uber-fx
    skill for application lifecycle, modules, and signal-aware Run() built on top of dig
  • → See
    samber/cc-skills-golang@golang-dependency-injection
    skill for DI concepts and library comparison
  • → See
    samber/cc-skills-golang@golang-samber-do
    skill for a generics-based alternative without reflection
  • → See
    samber/cc-skills-golang@golang-google-wire
    skill for compile-time DI (no runtime container)
  • → 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 uber-go/dig, open an issue at https://github.com/uber-go/dig/issues.
  • → 如需了解基于dig构建的应用生命周期、模块和支持信号处理的Run(),请查看
    samber/cc-skills-golang@golang-uber-fx
    技能
  • → 如需了解DI概念和库对比,请查看
    samber/cc-skills-golang@golang-dependency-injection
    技能
  • → 如需了解基于泛型的无反射替代方案,请查看
    samber/cc-skills-golang@golang-samber-do
    技能
  • → 如需了解编译时DI(无运行时容器),请查看
    samber/cc-skills-golang@golang-google-wire
    技能
  • → 如需了解接口设计模式,请查看
    samber/cc-skills-golang@golang-structs-interfaces
    技能
  • → 如需了解通用测试模式,请查看
    samber/cc-skills-golang@golang-testing
    技能
如果在使用uber-go/dig时遇到bug或意外行为,请在https://github.com/uber-go/dig/issues提交问题。