2.5 KiB
2.5 KiB
依赖注入
MengStack 使用 uber-go/fx 进行依赖注入,自动管理组件的生命周期和依赖关系。
为什么用依赖注入?
没有 DI 框架时,组装应用需要手动 wiring:
// 手动 wiring — 容易遗漏,难以维护
db := database.New(cfg.DB)
rdb := cache.New(cfg.Redis)
repo := infrastructure.NewUserRepo(db)
svc := application.NewService(repo, rdb, cfg.JWT)
handler := interfaces.NewHandler(svc)
使用 fx 后,依赖关系由框架自动解析:
// fx 自动解析依赖
fx.Provide(
database.New,
cache.New,
application.NewService,
interfaces.NewHandler,
)
fx.Module 组织
每个模块通过 fx.Module 声明自己的依赖:
// internal/modules/auth/interfaces/module.go
var Module = fx.Module("auth",
fx.Provide(
infrastructure.NewUserRepository,
infrastructure.NewTokenRepository,
application.NewService,
NewHandler,
NewAuthMiddleware,
SetupRoutes,
),
)
主应用组装所有模块:
// internal/app/app.go
func NewApp() *fx.App {
return fx.New(
fx.Provide(config.Load, logger.New),
database.Module,
cache.Module,
authinterfaces.Module,
Module,
)
}
生命周期管理
fx 自动管理组件的启动和停止顺序:
func newServer(lc fx.Lifecycle, engine *gin.Engine) *http.Server {
srv := &http.Server{Addr: ":2222", Handler: engine}
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 会确保:
- OnStart 按依赖顺序执行(先数据库,后应用)
- OnStop 按逆序执行(先应用,后数据库)
参数注入
通过函数参数声明依赖,fx 自动注入:
func newEngine(
cfg *config.Config, // fx 自动提供
log *zap.Logger, // fx 自动提供
db *gorm.DB, // fx 自动提供
rdb *redis.Client, // fx 自动提供
authHandler *Handler, // fx 自动提供
authMW gin.HandlerFunc, // fx 自动提供
) *gin.Engine {
// ...
}
最佳实践
- 构造函数注入:通过
fx.Provide注册构造函数 - 接口传递:尽量传递接口而非具体实现
- 模块隔离:每个业务模块声明自己的
fx.Module - 避免全局状态:所有依赖通过注入获取