Skip to content

依赖注入 ​

MengStack 使用 uber-go/fx 进行依赖注入,自动管理组件的生命周期和依赖关系。

为什么用依赖注入? ​

没有 DI 框架时,组装应用需要手动 wiring:

go
// 手动 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 后,依赖关系由框架自动解析:

go
// fx 自动解析依赖
fx.Provide(
    database.New,
    cache.New,
    application.NewService,
    interfaces.NewHandler,
)

fx.Module 组织 ​

每个模块通过 fx.Module 声明自己的依赖:

go
// internal/modules/auth/interfaces/module.go
var Module = fx.Module("auth",
    fx.Provide(
        infrastructure.NewUserRepository,
        infrastructure.NewTokenRepository,
        application.NewService,
        NewHandler,
        NewAuthMiddleware,
        SetupRoutes,
    ),
)

主应用组装所有模块:

go
// 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 自动管理组件的启动和停止顺序:

go
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 自动注入:

go
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 {
    // ...
}

最佳实践 ​

  1. 构造函数注入:通过 fx.Provide 注册构造函数
  2. 接口传递:尽量传递接口而非具体实现
  3. 模块隔离:每个业务模块声明自己的 fx.Module
  4. 避免全局状态:所有依赖通过注入获取