mengstack-website/guide/dependency-injection.md
2026-10-02 23:55:36 +08:00

112 lines
2.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 依赖注入
MengStack 使用 [uber-go/fx](https://github.com/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. **避免全局状态**:所有依赖通过注入获取