为 Go 项目挑选合适的依赖注入方式,并产出可直接套用的接线代码。
编程
golang-uber-dig
试用用 uber-go/dig 反射容器组装 Go 应用的对象图。
它能做什么
在 Go 应用中以 uber-go/dig 为反射式依赖注入容器组装对象图。通过 Provide 注册构造函数,用 Invoke 按需解析,借助 dig.In 与 dig.Out 结构体把多个输入输出聚合在一起。支持命名值、值组、可选依赖、作用域、Decorate,以及用 dig.As 把具体类型暴露成接口。构造函数是惰性且带记忆的——同一输出类型在每个容器里只构建一次,后续按单例复用。错误以返回值方式上报,并被 dig 包装上触发该错误的依赖路径,而非 panic。容器只适合放在组合根,不内置生命周期、模块系统或信号处理——这些能力由 uber-go/fx 在 dig 之上提供。
什么时候用它
- 在 Go 服务里引入 uber-go/dig,把容器放在 main 组合根
- 启动期用 Provide 与 Invoke 把整个依赖图连接起来
- 把 HTTP handler、健康检查、迁移脚本等聚合成值组统一消费
- 用 dig.As 把具体实现隐藏在接口后面,只暴露窄接口
技能文档
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.
Using uber-go/dig for Dependency Injection in 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.
go get go.uber.org/dig
dig vs. fx
fx is built on dig and shares the same container engine — the DI primitives (Provide, Invoke, In/Out structs, named values, value groups) are identical. fx.In/fx.Out are re-exports of dig.In/dig.Out.
What fx adds on top of dig:
| Concern | dig | fx |
|---|---|---|
| DI container | ✅ dig.New() | ✅ (embedded) |
| Lifecycle hooks | ❌ | ✅ fx.Lifecycle OnStart/OnStop |
| Module system | ❌ | ✅ fx.Module with scoped decorators |
| Signal-aware run loop | ❌ | ✅ app.Run() blocks on SIGINT/SIGTERM |
| Structured event logging | ❌ | ✅ fx.WithLogger / fxevent |
| Startup/shutdown timeout | ❌ | ✅ fx.StartTimeout / fx.StopTimeout |
Choose dig when you need the wiring graph only: CLI tools, libraries exposing a container to callers, test harnesses, or embedding DI into an existing app that manages its own lifecycle.
Choose fx for long-running services (HTTP servers, workers, daemons) — lifecycle and signal handling are non-negotiable there. See samber/cc-skills-golang@golang-uber-fx skill.
Container
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).
Provide and Invoke
// 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".
Parameter Objects with dig.In
Once a constructor has 4+ dependencies, embed dig.In to group them as struct fields and tag fields:
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:"...".
Result Objects with dig.Out
Return several values from one constructor and attach name/group tags to results:
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:
c.Provide(NewPrimaryDB, dig.Name("primary"))
c.Provide(NewReadOnlyDB, dig.Name("readonly"))
Consume by adding name:"primary" / name:"readonly" to a dig.In field.
Value Groups
Many providers, one consumer slice — typical for HTTP handlers, health checks, migrations:
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.
Provide as Interface (dig.As)
Register a concrete constructor and expose it under one or more interfaces without a separate adapter:
c.Provide(NewPostgresDB, dig.As(new(Database), new(io.Closer)))
// Consumers ask for Database or io.Closer; *PostgresDB stays hidden.
Full Application Example
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.
Best Practices
- Keep the container at the composition root — never pass
*dig.Containeras a parameter; treat it like a plumbing detail ofmain(). Service-locator patterns defeat the testability gains of DI. - Depend on interfaces, not concrete types — lets you swap implementations in tests without touching production code, and lets you use
dig.Asto expose narrow interfaces from wide structs. - Prefer parameter objects (
dig.Instructs) 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. - Group registration by module (one file per module that calls
c.Providefor its types) — review and refactoring become a per-module concern, and you can extract a module into a fx.Module later without rewriting wiring. - Validate the graph eagerly in tests — call
c.Invokeagainst the composition root in CI to surface missing providers at boot time, not at first request.DryRun(true)skips constructor execution. - Return errors from constructors instead of panicking — dig wraps them with the dependency path, which makes the failure point obvious.
Common Mistakes
| Mistake | Fix |
|---|---|
| Passing the container into services | The 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 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 init() empty — start work only inside the constructor, after the graph is built. |
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.
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
Cross-References
- → See
samber/cc-skills-golang@golang-uber-fxskill for application lifecycle, modules, and signal-aware Run() built on top of dig - → See
samber/cc-skills-golang@golang-dependency-injectionskill for DI concepts and library comparison - → See
samber/cc-skills-golang@golang-samber-doskill for a generics-based alternative without reflection - → See
samber/cc-skills-golang@golang-google-wireskill for compile-time DI (no runtime container) - → See
samber/cc-skills-golang@golang-structs-interfacesskill for interface design patterns - → See
samber/cc-skills-golang@golang-testingskill for general testing patterns
If you encounter a bug or unexpected behavior in uber-go/dig, open an issue at .
常见问题
- dig 自带生命周期钩子或信号处理吗?
- 不带。dig 只是 DI 引擎——没有 OnStart/OnStop,没有模块系统,也没有信号感知的运行循环。长跑服务( HTTP server、worker、守护进程 )建议在 dig 之上用 uber-go/fx。
- 什么时候该用 dig.In 而不是普通参数?
- 构造函数依赖达到 4 个以上时。把 dig.In 嵌入结构体,用 name:、optional:"true"、group:"..." 等字段标签把命名值、可选依赖、值组统一聚合,新增依赖也只需加一行字段。
- 构造函数会被多次调用吗?
- 不会。构造函数是惰性且带记忆的,同一输出类型在每个容器里只构造一次,后续解析都复用同一个实例,等同容器内单例。
相关技能
用 uber-go/fx 装配 Go 长时服务:DI 容器、生命周期钩子、模块化、信号感知 Run。
使用 google/wire 为 Go 项目做编译期依赖注入,通过代码生成在编译前捕获缺失依赖。
使用 samber/do v2 为 Go 项目搭建类型安全的依赖注入容器
Golang package and module documentation and exploration via `godig`, a pkg.go.dev API client (CLI + MCP server) — package docs, API references, symbols, code examples, available versions, importers (who imports a package), licenses, and known vulnerabilities. Read-only, no auth. Use for looking up any Go/Golang library's documentation, API signatures, usage examples, which versions exist, whether a dependency has CVEs, or who imports a package — prefer this over Context7 for any Go package or module. Triggers on: how to use a Go library, Go API docs, import usage, code examples, pkg.go.dev. Not for upgrading dependencies (→ See `samber/cc-skills-golang@golang-dependency-management` skill) or choosing a library (→ See `samber/cc-skills-golang@golang-popular-libraries` skill). Not for local symbols, or for navigating an already-used dependency's resolved source, call sites, or generic instantiations — → See `samber/cc-skills-golang@golang-gopls` skill for those.
Recommends production-ready Golang libraries and frameworks. Apply when the user explicitly asks for library suggestions, wants to compare alternatives, needs to choose a library for a specific task, or when a new dependency is being added to the project.