fx依赖注入的使用

fx 依赖注入的使用


最近手里的一项工作是将老的项目迁移到新的大仓, 且代码规范符合新的要求, 包括依赖注入使用fx, 使用新的devops, 新的监控组件等
借这个机会深入学习了下fx, 在这里做个学习记录, 行文思路完全依据我的学习路径,如有谬误,感谢大佬们指出

1. 为啥需要依赖注入?

没学过Java/Spring的同学第一次接触依赖注入的同学肯定很迷茫, 啥叫依赖注入啊,以前只听过sql注入, 但是两者好像风马牛不相及, 也确实..这两者没啥关系

让我用一句话来讲, 就是省略你一堆的的initXXX函数.

例如,参照以下代码,我想初始化一个服务, 需要各类各样的依赖,数据库,缓存,复杂的业务结构…虽然下述代码看起来并不复杂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func InitDB()*gorm.DB{...}
func InitRedis()*redis.Client{...}
func InitEs()es *elasticSearch.Client{...}
func InitRouter()*Router
func InitHttp(r *Router)*Http.Server{
s = &http.Server{...}
s.Router = r
}

func initExampleService(db *gorm.DB, rds *redis.Client, es *elasticSearch.Client,s *Http.Server, ...){
example.db = db
example.redis = rds
...
s.serve()
}

但随着项目复杂度的提升,业务逻辑的层叠, 这一堆init会变越来越多, 代码项目启动入口会变得重复化代码极多, 充斥着大量知识构造struct的语句,
于是在这种背景下, 依赖注入的思想应运而生,如果我只告诉函数需要的类型, 函数就可以自己自己组装需要的结构,岂不代码变清晰了,也节省了很多重复的工作.

依赖注入就是干这个事情的, 你只需要写基本的结构,例如函数定义, 依赖注入自动帮你寻找需要的依赖并构造. 对大型项目非常友好, 但我也认为在并不复杂的项目中引入依赖注入,并没有必要,反而会造成简单逻辑复杂化.

2. 依赖注入的常见方案

1.wire

https://github.com/google/wire

wire是代码生成派的, 告诉wire你要的方法要做什么, wire会帮你生成代码,你再去调用他生成的方法,很简单,不赘述了

文档写的很详细,就不在此重复了,wire.Build()指定好需要new的依赖,一般实践中,我们会将wire的方法定义与生成代码放在同一目录下,方便管理.

文档:https://go.dev/blog/wire
https://github.com/google/wire/blob/main/_tutorial/README.md

2. fx

https://github.com/uber-go/fx
依赖注入通过解耦对象创建与使用,实现控制反转(IoC)。在Java/Spring体系中,注解和IoC容器已成为标配。而在Go语言中,由于缺乏官方依赖注入框架,开发者往往陷入手动注入的泥潭。Fx框架的出现填补了这一空白。

相比于wire的代码生成,fx是通过反射实现注入的,通过类型反射寻找参数,函数和接口.

3. fx 的常用操作

1
2
3
4
5
6
// 核心API一览
app := fx.New(
fx.Provide(NewService, NewLogger), // 依赖提供者
fx.Invoke(RunServer, InitDB), // 依赖消费者
fx.Lifecycle, // 生命周期管理
)

3.1 依赖提供者(fx.Provide)

通过普通函数或匿名函数注册依赖:

1
2
3
4
5
6
7
8
func NewLogger() (*zap.Logger, error) {
config := zap.NewProductionConfig()
return config.Build()
}

app := fx.New(
fx.Provide(NewLogger), // 注册单例服务
)

3.2 依赖注入(fx.Invoke)

通过函数参数自动注入依赖:

1
2
3
4
5
6
7
8
func RunServer(logger *zap.Logger, port int) {
logger.Info("Server started", zap.Int("port", port))
}

app := fx.New(
fx.Provide(NewLogger),
fx.Invoke(RunServer),
)

3.3. 生命周期管理(fx.Lifecycle)

通过Hook实现精准的资源管控:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func main() {
app := fx.New(
fx.Provide(NewDB),
fx.Invoke(RegisterHooks),
)
}

func RegisterHooks(lc fx.Lifecycle, db *sql.DB) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
go db.Migrate() // 启动时迁移数据库
return nil
},
OnStop: func(ctx context.Context) error {
db.Close() // 停止时关闭连接
return nil
},
})
}

3.4 静态配置注入(fx.Supply)

直接注入预定义对象:

1
2
3
4
5
6
7
8
9
10
var config = struct {
Port int `json:"port"`
}{
Port: 8080,
}

app := fx.New(
fx.Supply(config),
fx.Invoke(StartServer),
)

3.5 多实例管理(fx.Annotated)

通过标签区分同名依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type Logger interface {
Log(string)
}

// 注册不同实现
fx.Provide(
fx.Annotated{
Name: "file",
Target: func() Logger { return &FileLogger{} },
},
fx.Annotated{
Name: "console",
Target: func() Logger { return &ConsoleLogger{} },
},
)

// 按需注入
func ProcessData(logger Logger `name:"console"`) {
logger.Log("Processing...")
}

3.6 参数聚合(fx.In/Out)

简化复杂依赖结构:

1
2
3
4
5
6
7
8
9
type ServiceParams struct {
fx.In
DB *sql.DB
Logger Logger
}

func NewService(p ServiceParams) *Service {
return &Service{DB: p.DB, Logger: p.Logger}
}

4.实战案例:构建RESTful API服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main

import (
"net/http"

"go.uber.org/fx"
"github.com/gin-gonic/gin"
)

type Config struct {
Port int `json:"port"`
}

func main() {
app := fx.New(
fx.Provide(
ProvideConfig,
gin.Default,
),
fx.Invoke(RegisterRoutes),
)

app.Run()
}

func ProvideConfig() Config {
return Config{Port: 8080}
}

func RegisterRoutes(router *gin.Engine) {
router.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
}

5.最佳实践与生态扩展

1. 模块化设计(fx.Module)

将功能拆分为独立模块:

1
2
3
4
5
6
7
8
9
10
11
var AuthModule = fx.Options(
fx.Provide(NewAuthService),
fx.Invoke(RegisterAuthRoutes),
)

func main() {
app := fx.New(
AuthModule,
fx.Invoke(StartServer),
)
}

总结

Fx框架通过声明式编程模型,将复杂的依赖管理转化为简洁的API调用。无论是基础的服务构建还是复杂的微服务架构,Fx都能提供优雅的解决方案。随着Go语言生态的成熟,Fx必将成为构建可维护系统的利器。建议开发者深入学习其模块化设计和生命周期管理特性,打造高内聚低耦合的应用架构。



fx依赖注入的使用
http://bestcrr.com/2024/12/12/fx依赖注入/
作者
sherwin liu
发布于
2024年12月12日
许可协议