Go mod 好菜系列 - 0x0C fx 这锅生命周期和装配更重一点
从模块化、生命周期、依赖装配和项目体量角度详细聊 fx,看看它为什么适合更重一点的服务项目,又为什么不该滥用。
如果说 wire 是“把装配代码在编译期生成出来”,那 fx 则更像是另一条路线:它不只是想帮你装配依赖,还想把模块、生命周期、启动和关闭过程也一起纳进来。也正因为这样,它比 wire 更重,也更看项目体量。
fx 在解决什么问题
它解决的不是“我能不能 new 出几个对象”,而是“一个服务型项目的组件怎么统一提供、统一装配、统一启动、统一关闭”。
也就是说,fx 关注的不只是依赖注入,还有:
- 模块注册
- 对象提供
- 调用执行
- 生命周期钩子
为什么它会显得比 wire 更重
因为它不只是一个生成器,而更像一个应用容器框架。你会更明显地感受到“项目有一层专门的装配和运行组织结构”。
一个很简化的感觉
app := fx.New(
fx.Provide(
NewConfig,
NewLogger,
NewDB,
NewUserRepo,
NewUserService,
NewHTTPServer,
),
fx.Invoke(RegisterRoutes),
)
app.Run()这个写法的关键味道在于:你开始不是一个对象一个对象手动往下串了,而是把“谁提供什么、谁启动什么”集中交给了容器层。
生命周期是它特别值得注意的一点
func NewHTTPServer(lc fx.Lifecycle) *http.Server {
srv := &http.Server{Addr: ":8080"}
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
go srv.ListenAndServe()
return nil
},
OnStop: func(ctx context.Context) error {
return srv.Shutdown(ctx)
},
})
return srv
}这也是 fx 和很多“纯注入工具”最不一样的地方:它很关心服务怎么启动,也很关心服务怎么结束。
什么时候它特别合适
- 项目组件很多
- 服务生命周期复杂
- 你需要模块化启动和关闭
- 团队已经接受容器式组织方式
比如大型服务、平台型项目、多个基础设施组件同时参与启动时,fx 的优势会明显很多。
什么时候它会显得用力过猛
如果项目还很小、依赖层级不深、启动流程很简单,那 fx 很容易让人产生一种“服务还没复杂,装配先复杂了”的感觉。
这不是 fx 不好,而是它天然就更适合中大型服务,而不是所有 Go 项目的默认起手式。
fx 和 wire 的区别怎么粗暴理解
- wire 更偏编译期装配工具
- fx 更偏应用级装配和生命周期框架
两者都在解决“依赖怎么组织”的问题,但 fx 管得更宽,代价也更重。
小结
fx 这道菜更适合项目已经开始往平台化、模块化方向长的时候:
- 它不只管依赖装配,还管生命周期
- 特别适合启动和关闭都比较复杂的服务
- 项目小时很容易显重
- 它更像“整套餐具”,不是一把小勺子
到这里,第二批 Go mod 好菜系列 就算补上了一条新的分支:从参数校验、JWT,到 sqlx、ent,再到 wire 和 fx,基本把“常用库”和“工程装配”这两条线都接起来了。