golang-uber-fx
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePersona: You are a Go architect building a long-running service with fx. You wire the graph at the composition root, push lifecycle into hooks instead of , and treat modules as the unit of reuse.
init()角色定位: 你是一名使用fx构建长期运行服务的Go架构师。你在组合根处编排依赖图,将生命周期逻辑移入钩子而非函数,并将模块作为复用单元。
init()Using uber-go/fx for Application Wiring in Go
使用uber-go/fx进行Go应用编排
Application framework combining a reflection-based DI container (built on ) with a lifecycle, module system, signal-aware run loop, and structured event logging. For long-running services where boot order, graceful shutdown, and modular composition matter.
uber-go/digOfficial 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/fxfx vs. dig. fx wraps dig and adds lifecycle hooks (), modules (fx.Lifecycle),fx.Modulewith signal handling, structured event logs (Run()), and ergonomic helpers. Use raw dig (fxeventskill) only when you don't need lifecycle or app boot — most production services should use fx.samber/cc-skills-golang@golang-uber-dig
该应用框架结合了基于反射的DI容器(构建于之上)、生命周期管理、模块系统、支持信号处理的运行循环以及结构化事件日志功能。适用于启动顺序、优雅关闭和模块化组合至关重要的长期运行服务。
uber-go/dig官方资源:
本技能内容并非详尽无遗。如需更多信息,请参考库文档和代码示例。Context7可作为发现平台提供帮助。
bash
go get go.uber.org/fxfx vs. dig:fx封装了dig,并添加了生命周期钩子()、模块(fx.Lifecycle)、带信号处理的fx.Module方法、结构化事件日志(Run())以及易用的辅助工具。仅当不需要生命周期或应用启动管理时才使用原生dig(fxevent技能)——大多数生产服务应使用fx。samber/cc-skills-golang@golang-uber-dig
The Application
应用示例
go
import "go.uber.org/fx"
app := fx.New(
fx.Provide(NewLogger, NewDatabase, NewServer),
fx.Invoke(RegisterRoutes),
)
app.Run() // blocks until SIGINT/SIGTERM, then runs OnStop hooksBoot stages: validates types (constructors do not run); runs each and fires OnStart hooks in topological order; main blocks on ; fires OnStop hooks in reverse order. Default timeout is 15 seconds — override with / .
fx.Newapp.Start(ctx)fx.Invokeapp.Done()app.Stop(ctx)fx.StartTimeoutfx.StopTimeoutgo
import "go.uber.org/fx"
app := fx.New(
fx.Provide(NewLogger, NewDatabase, NewServer),
fx.Invoke(RegisterRoutes),
)
app.Run() // 阻塞直到收到SIGINT/SIGTERM信号,随后执行OnStop钩子启动阶段:验证类型(构造函数不会运行);按拓扑顺序执行每个并触发OnStart钩子;主线程阻塞在;按逆序触发OnStop钩子。默认超时时间为15秒——可通过 / 覆盖。
fx.Newapp.Start(ctx)fx.Invokeapp.Done()app.Stop(ctx)fx.StartTimeoutfx.StopTimeoutProvide and Invoke
Provide与Invoke
go
fx.New(
fx.Provide(NewLogger, NewDatabase, NewServer), // lazy
fx.Invoke(RegisterRoutes, StartMetricsExporter), // always run during Start
)fx.Providefx.Invokego
fx.New(
fx.Provide(NewLogger, NewDatabase, NewServer), // 延迟初始化
fx.Invoke(RegisterRoutes, StartMetricsExporter), // 启动阶段始终执行
)fx.Providefx.InvokeLifecycle Hooks
生命周期钩子
Inject and append hooks. Constructors should return quickly; long-running work belongs in .
fx.LifecycleOnStartgo
func NewHTTPServer(lc fx.Lifecycle, log *zap.Logger, cfg *Config) *http.Server {
srv := &http.Server{Addr: cfg.Addr}
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
ln, err := net.Listen("tcp", srv.Addr)
if err != nil { return err }
go srv.Serve(ln) // blocking work in a goroutine
return nil
},
OnStop: func(ctx context.Context) error {
return srv.Shutdown(ctx)
},
})
return srv
}Both callbacks receive a context bounded by / — respect cancellation. OnStart must return quickly — spawn a goroutine for blocking work; otherwise startup hangs and dependent hooks never fire.
StartTimeoutStopTimeoutfx.StartHookfx.StopHookfx.StartStopHookgo
lc.Append(fx.StartStopHook(srv.Start, srv.Stop)) // matched pair注入并添加钩子。构造函数应快速返回;长期运行的任务应放在中。
fx.LifecycleOnStartgo
func NewHTTPServer(lc fx.Lifecycle, log *zap.Logger, cfg *Config) *http.Server {
srv := &http.Server{Addr: cfg.Addr}
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
ln, err := net.Listen("tcp", srv.Addr)
if err != nil { return err }
go srv.Serve(ln) // 在goroutine中执行阻塞任务
return nil
},
OnStop: func(ctx context.Context) error {
return srv.Shutdown(ctx)
},
})
return srv
}两个回调函数都会接收受/限制的context——请遵守取消机制。OnStart必须快速返回——将阻塞任务放在goroutine中;否则启动过程会挂起,依赖的钩子永远不会触发。
StartTimeoutStopTimeoutfx.StartHookfx.StopHookfx.StartStopHookgo
lc.Append(fx.StartStopHook(srv.Start, srv.Stop)) // 匹配的钩子对Parameter and Result Objects
参数与结果对象
fx re-exports dig's / as / . Use them when a constructor has 4+ dependencies, or when you need // tags.
dig.Indig.Outfx.Infx.Outnamegroupoptionalgo
type ServerParams struct {
fx.In
Logger *zap.Logger
DB *sql.DB
Cache *redis.Client `optional:"true"`
Routes []http.Handler `group:"routes"`
}
func NewServer(p ServerParams) *Server { /* ... */ }fx将dig的 / 重新导出为 / 。当构造函数有4个及以上依赖,或需要//标签时使用它们。
dig.Indig.Outfx.Infx.Outnamegroupoptionalgo
type ServerParams struct {
fx.In
Logger *zap.Logger
DB *sql.DB
Cache *redis.Client `optional:"true"`
Routes []http.Handler `group:"routes"`
}
func NewServer(p ServerParams) *Server { /* ... */ }fx.Annotate
fx.Annotate
fx.Annotatefx.Outgo
fx.Provide(
fx.Annotate(NewPrimaryDB, fx.ResultTags(`name:"primary"`)),
fx.Annotate(NewPostgresDB, fx.As(new(Database))), // expose interface
fx.Annotate(NewUserHandler,
fx.As(new(http.Handler)),
fx.ResultTags(`group:"routes"`),
),
)fx.Annotatefx.Outgo
fx.Provide(
fx.Annotate(NewPrimaryDB, fx.ResultTags(`name:"primary"`)),
fx.Annotate(NewPostgresDB, fx.As(new(Database))), // 暴露接口
fx.Annotate(NewUserHandler,
fx.As(new(http.Handler)),
fx.ResultTags(`group:"routes"`),
),
)Value Groups
值分组
Many constructors, one consumer slice — typical for routes, health checks, metrics collectors:
go
type RouteResult struct {
fx.Out
Handler http.Handler `group:"routes"`
}
type ServerParams struct {
fx.In
Routes []http.Handler `group:"routes"`
}Append () to unwrap a slice instead of nesting it. Order is not guaranteed — provide an explicit ordered slice when sequence matters.
,flattengroup:"routes,flatten"多个构造函数对应一个消费者切片——典型场景如路由、健康检查、指标收集器:
go
type RouteResult struct {
fx.Out
Handler http.Handler `group:"routes"`
}
type ServerParams struct {
fx.In
Routes []http.Handler `group:"routes"`
}添加()可展开切片而非嵌套。顺序不保证——当顺序重要时,需从单个构造函数提供显式有序切片。
,flattengroup:"routes,flatten"fx.Module
fx.Module
fx.Modulefx.Module("db", ...)go
var DatabaseModule = fx.Module("database",
fx.Provide(NewConnection, NewUserRepository),
fx.Decorate(func(log *zap.Logger) *zap.Logger {
return log.Named("db")
}),
)
func main() {
fx.New(
fx.Provide(NewConfig, NewLogger),
DatabaseModule,
HTTPModule,
).Run()
}Treat each module as a small library that can be lifted into another app — its public surface is the types it Provides.
For //, optional deps, custom logging, manual lifecycle, and Quick Reference, see advanced.md.
fx.Supplyfx.Replacefx.Decoratefx.Modulefx.Module("db", ...)go
var DatabaseModule = fx.Module("database",
fx.Provide(NewConnection, NewUserRepository),
fx.Decorate(func(log *zap.Logger) *zap.Logger {
return log.Named("db")
}),
)
func main() {
fx.New(
fx.Provide(NewConfig, NewLogger),
DatabaseModule,
HTTPModule,
).Run()
}将每个模块视为可移植到其他应用的小型库——它对外暴露的类型即为其公共接口。
关于//、可选依赖、自定义日志、手动生命周期管理和快速参考,请查看advanced.md。
fx.Supplyfx.Replacefx.DecorateBest Practices
最佳实践
- Keep thin — providers, modules, and a single
main(). Push real work into modules so each can be tested in isolation.Run() - Use lifecycle hooks instead of or goroutines launched from constructors — Start/Stop ordering depends on graph topology, but
init()goroutines do not, which leads to races and leaks.init() - OnStart must return promptly — long work goes in a goroutine inside the hook. A blocking OnStart hangs the rest of the boot.
- Respect in hooks — a hook that ignores cancellation is reported as a timeout failure but its goroutine continues, leaking resources.
ctx.Done() - Group by module, not by layer — a module owns the providers, lifecycle, and decorators for one concern (HTTP, DB, metrics).
- Use for tags rather than wrapping a constructor in an
fx.Annotatestruct — keeps the constructor reusable outside fx.fx.Out - Replace with
fx.Providefor pre-built values (config, command-line flags). Shorter, signals intent.fx.Supply - Validate the graph in CI by booting under — catches missing providers and cycles before deploy.
fx.New(...).Err()
- 保持简洁——仅包含提供者、模块和单个
main()。将实际业务逻辑移入模块,以便每个模块可独立测试。Run() - 使用生命周期钩子而非或从构造函数启动的goroutine——启动/停止顺序依赖于依赖图拓扑,但
init()中的goroutine不遵循此顺序,会导致竞态条件和资源泄漏。init() - OnStart必须及时返回——长期运行的任务应放在钩子内的goroutine中。阻塞的OnStart会导致整个启动过程挂起。
- 在钩子中遵守——忽略取消机制的钩子会被报告为超时失败,但其goroutine仍会继续运行,造成资源泄漏。
ctx.Done() - 按模块分组而非按分层——每个模块负责一个关注点(HTTP、数据库、指标)的提供者、生命周期和装饰器。
- 使用添加标签,而非用
fx.Annotate结构体封装构造函数——这样可保持构造函数在fx之外的复用性。fx.Out - 对于预构建值(配置、命令行参数),用替代
fx.Supply。更简洁,且能明确表达意图。fx.Provide - 在CI中通过启动应用来验证依赖图——在部署前捕获缺失的提供者和循环依赖。
fx.New(...).Err()
Common Mistakes
常见错误
| Mistake | Fix |
|---|---|
| Long-running work directly in OnStart | Spawn a goroutine inside OnStart; the hook itself must return quickly so dependent hooks can run. |
| Pre-built values (config, secrets) belong in |
| Module decorator leaking to siblings | Decorate inside |
| Group order assumed | Groups are unordered. If order matters, provide an ordered slice from one constructor. |
| Constructors with side effects | Side effects belong in OnStart — constructors should be cheap and pure-ish, since they may run concurrently and lazily. |
Forgotten | Without an Invoke (or downstream consumer), constructors never run. Add at least one Invoke per app. |
| 错误 | 修复方案 |
|---|---|
| 在OnStart中直接执行长期运行的任务 | 在OnStart内部启动goroutine;钩子本身必须快速返回,以便依赖的钩子可以运行。 |
用 | 预构建值(配置、密钥)应放在 |
| 模块装饰器影响到同级模块 | 在 |
| 假设分组顺序是固定的 | 分组是无序的。如果顺序重要,从单个构造函数提供显式有序的切片。 |
| 构造函数带有副作用 | 副作用应放在OnStart中——构造函数应轻量化且接近纯函数,因为它们可能并发且延迟执行。 |
忘记添加 | 如果没有Invoke(或下游消费者),构造函数永远不会运行。每个应用至少添加一个Invoke。 |
Testing
测试
Use to integrate fx with (failures call , registers as ). pulls values out of the graph; swaps real dependencies for fakes. Full patterns in testing.md.
go.uber.org/fx/fxtest*testing.Tt.FatalRequireStopt.Cleanupfx.Populate(&target)fx.Replace使用将fx与集成(失败时调用,注册为)。从依赖图中提取值;将真实依赖替换为模拟实现。完整模式请查看testing.md。
go.uber.org/fx/fxtest*testing.Tt.FatalRequireStopt.Cleanupfx.Populate(&target)fx.ReplaceFurther Reading
扩展阅读
- advanced.md — Supply/Replace/Decorate, optional deps, custom event logging, manual lifecycle, full Quick Reference
- recipes.md — full HTTP service with database/metrics, background workers with graceful drain, multiple impls of the same interface, manual lifecycle for CLI embedding
- testing.md — fxtest patterns, ,
fx.Replace, isolated lifecycle tests, CI graph validationfx.Populate
- advanced.md —— Supply/Replace/Decorate、可选依赖、自定义事件日志、手动生命周期管理、完整快速参考
- recipes.md —— 包含数据库/指标的完整HTTP服务、支持优雅关闭的后台工作器、同一接口的多个实现、用于CLI嵌入的手动生命周期管理
- testing.md —— fxtest模式、、
fx.Replace、独立生命周期测试、CI依赖图验证fx.Populate
Cross-References
交叉参考
- → See skill for the underlying container,
samber/cc-skills-golang@golang-uber-dig/dig.In, and DI without lifecycledig.Out - → 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 context propagation in OnStart/OnStop hooks
samber/cc-skills-golang@golang-context - → See skill for general testing patterns
samber/cc-skills-golang@golang-testing
If you encounter a bug or unexpected behavior in uber-go/fx, open an issue at https://github.com/uber-go/fx/issues.
- → 如需底层容器、/
dig.In以及无生命周期的DI方案,请查看dig.Out技能samber/cc-skills-golang@golang-uber-dig - → 如需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 - → 如需OnStart/OnStop钩子中的context传播,请查看技能
samber/cc-skills-golang@golang-context - → 如需通用测试模式,请查看技能
samber/cc-skills-golang@golang-testing
如果在使用uber-go/fx时遇到bug或意外行为,请在https://github.com/uber-go/fx/issues提交问题。