golang-uber-dig
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePersona: 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 ) and resolve object graphs during startup.
uber-go/fxOfficial 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/digWhen 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 (skill) — it adds lifecycle hooks, modules, and signal-awaresamber/cc-skills-golang@golang-uber-fxon top of the same dig engine.Run()
基于反射的DI工具包,旨在为应用框架提供支持(它是的底层引擎),并在启动时解析对象依赖图。
uber-go/fx官方资源:
本技能内容并非详尽无遗。如需更多信息,请参考库文档和代码示例。Context7可作为发现平台提供帮助。
bash
go get go.uber.org/dig何时选择dig而非fx:仅当你需要依赖注入图,而不需要fx的生命周期、信号处理或应用启动语义时,才使用原生dig。对于大多数生产应用,优先选择fx(技能)——它在同一dig引擎之上添加了生命周期钩子、模块以及支持信号处理的samber/cc-skills-golang@golang-uber-fx方法。Run()
Container
容器
go
import "go.uber.org/dig"
c := dig.New()Useful options: (faster startup), (turn panics into ), (validate without invoking).
dig.DeferAcyclicVerification()dig.RecoverFromPanics()dig.PanicErrordig.DryRun(true)go
import "go.uber.org/dig"
c := dig.New()实用选项:(启动更快)、(将恐慌转换为)、(仅验证不执行)。
dig.DeferAcyclicVerification()dig.RecoverFromPanics()dig.PanicErrordig.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). errors at registration if the constructor is malformed; returns the constructor's error wrapped with the dependency path that triggered it.
ProvideInvokeA dig constructor is any function. Inputs are dependencies, outputs are provided types. (last return) signals construction failure. Follow "accept interfaces, return structs".
errorgo
// 注册构造函数——懒加载,仅在需要其输出时执行
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()
})构造函数是懒加载且记忆化的:每种输出类型仅构建一次并共享(每个容器对应一个单例)。如果构造函数格式错误,会在注册时返回错误;会返回构造函数的错误,并附上触发该错误的依赖路径。
ProvideInvoke任何函数都可以作为dig构造函数。输入为依赖项,输出为提供的类型。最后一个返回值表示构造失败。遵循“依赖接口,返回结构体”的原则。
errorParameter Objects with dig.In
dig.In使用dig.In
的参数对象
dig.InOnce a constructor has 4+ dependencies, embed to group them as struct fields and tag fields:
dig.Ingo
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.Ingo
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使用dig.Out
的结果对象
dig.OutReturn several values from one constructor and attach / tags to results:
namegroupgo
type ConnResult struct {
dig.Out
ReadWrite *sql.DB `name:"primary"`
ReadOnly *sql.DB `name:"readonly"`
}
func NewConnections(cfg *Config) (ConnResult, error) { /* ... */ }从一个构造函数返回多个值,并为结果附加/标签:
namegroupgo
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.Namego
c.Provide(NewPrimaryDB, dig.Name("primary"))
c.Provide(NewReadOnlyDB, dig.Name("readonly"))Consume by adding / to a field.
name:"primary"name:"readonly"dig.In同一类型的两个提供者会产生冲突。使用来区分:
dig.Namego
c.Provide(NewPrimaryDB, dig.Name("primary"))
c.Provide(NewReadOnlyDB, dig.Name("readonly"))通过在字段上添加 / 来使用命名值。
dig.Inname:"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 (e.g. ) 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.
,flattengroup:"routes,flatten"多个提供者对应一个消费者切片——典型场景如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"`
}扁平化——添加(例如)来展开切片而非嵌套。值组的顺序不保证;如果顺序重要,请通过单个构造函数提供显式排序的切片。
,flattengroup:"routes,flatten"Provide as Interface (dig.As
)
dig.As以接口形式提供(dig.As
)
dig.AsRegister 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 skill.
samber/cc-skills-golang@golang-uber-fxFor 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
最佳实践
- Keep the container at the composition root — never pass as a parameter; treat it like a plumbing detail of
*dig.Container. Service-locator patterns defeat the testability gains of DI.main() - Depend on interfaces, not concrete types — lets you swap implementations in tests without touching production code, and lets you use to expose narrow interfaces from wide structs.
dig.As - Prefer parameter objects (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.
dig.In - Group registration by module (one file per module that calls 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.
c.Provide - Validate the graph eagerly in tests — call against the composition root in CI to surface missing providers at boot time, not at first request.
c.Invokeskips constructor execution.DryRun(true) - Return errors from constructors instead of panicking — dig wraps them with the dependency path, which makes the failure point obvious.
- 将容器置于组合根目录——切勿将作为参数传递;将其视为
*dig.Container的底层实现细节。服务定位器模式会抵消DI带来的可测试性优势。main() - 依赖接口而非具体类型——这让你无需修改生产代码即可在测试中替换实现,还能使用从宽泛的结构体中暴露窄接口。
dig.As - 当构造函数有4个及以上依赖项时,优先使用参数对象(结构体)——调用点保持可读性,添加新依赖只需一行修改,无需更改函数签名。
dig.In - 按模块分组注册(每个模块对应一个调用注册其类型的文件)——代码审查和重构可按模块进行,之后无需重写依赖注入代码即可将模块提取为fx.Module。
c.Provide - 在测试中提前验证依赖图——在CI中针对组合根目录调用,在启动时发现缺失的提供者,而非在首次请求时。
c.Invoke会跳过构造函数执行。DryRun(true) - 从构造函数返回错误而非恐慌——dig会将错误与依赖路径一起包装,让故障点一目了然。
Common Mistakes
常见错误
| Mistake | Fix |
|---|---|
| Passing the container into services | The container belongs to |
Two providers for the same type without | dig errors at |
Ignoring | Wrap each |
| Using groups when ordering matters | Groups are unordered. If order matters (middleware chain, migration sequence), provide an explicit ordered slice with one constructor. |
| Constructors with side effects on import | Keep |
| 错误 | 修复方案 |
|---|---|
| 将容器传入服务中 | 容器属于 |
同一类型的两个提供者未使用 | dig会在 |
忽略 | 用 |
| 在顺序重要的场景使用值组 | 值组是无序的。如果顺序重要(中间件链、迁移序列),请通过单个构造函数提供显式排序的切片。 |
| 导入时构造函数产生副作用 | 保持 |
Testing
测试
dig containers are cheap — build a fresh one per test, override providers with , and call 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.
DecorateInvokedig容器成本低廉——为每个测试构建一个新容器,使用覆盖提供者,调用驱动系统。如需完整模式(每个测试的依赖注入、共享助手、CI中的依赖图验证、断言注入时的错误、从构造函数恐慌中恢复),请查看testing.md。
DecorateInvokeFurther 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 skill for application lifecycle, modules, and signal-aware Run() built on top of dig
samber/cc-skills-golang@golang-uber-fx - → See skill for DI concepts and library comparison
samber/cc-skills-golang@golang-dependency-injection - → See skill for a generics-based alternative without reflection
samber/cc-skills-golang@golang-samber-do - → See skill for compile-time DI (no runtime container)
samber/cc-skills-golang@golang-google-wire - → See skill for interface design patterns
samber/cc-skills-golang@golang-structs-interfaces - → See skill for general testing patterns
samber/cc-skills-golang@golang-testing
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提交问题。