golang基于泛型的依赖注入框架插件do的使用
Golang基于泛型的依赖注入框架插件do的使用
简介
do是一个基于Go 1.18+泛型的依赖注入工具包,实现了依赖注入设计模式。它可以替代uber/dig
包在简单的Go项目中使用,并提供了类型安全的API。
特性
- 服务注册
- 服务调用
- 服务健康检查
- 服务关闭
- 服务生命周期钩子
- 命名或匿名服务
- 立即或延迟加载服务
- 依赖图解析
- 默认注入器
- 注入器克隆
- 服务覆盖
- 轻量级,无依赖
- 无需代码生成
安装
go get github.com/samber/do@v1
快速开始
基本示例
import "github.com/samber/do"
func main() {
injector := do.New()
// 提供CarService
do.Provide(injector, NewCarService)
// 提供EngineService
do.Provide(injector, NewEngineService)
car := do.MustInvoke[*CarService](injector)
car.Start()
// 输出: "car starting"
do.HealthCheck[EngineService](injector)
// 返回: "engine broken"
injector.Shutdown()
// 输出: "car stopped"
}
服务定义
type EngineService interface{}
func NewEngineService(i *do.Injector) (EngineService, error) {
return &engineServiceImplem{}, nil
}
type engineServiceImplem struct {}
// [可选] 实现do.Healthcheckable接口
func (c *engineServiceImplem) HealthCheck() error {
return fmt.Errorf("engine broken")
}
func NewCarService(i *do.Injector) (*CarService, error) {
engine := do.MustInvoke[EngineService](i)
car := CarService{Engine: engine}
return &car, nil
}
type CarService struct {
Engine EngineService
}
func (c *CarService) Start() {
println("car starting")
}
// [可选] 实现do.Shutdownable接口
func (c *CarService) Shutdown() error {
println("car stopped")
return nil
}
详细用法
服务注册
匿名服务,延迟加载
type DBService struct {
db *sql.DB
}
do.Provide[DBService](injector, func(i *Injector) (*DBService, error) {
db, err := sql.Open(...)
if err != nil {
return nil, err
}
return &DBService{db: db}, nil
})
命名服务,延迟加载
do.ProvideNamed(injector, "dbconn", func(i *Injector) (*DBService, error) {
db, err := sql.Open(...)
if err != nil {
return nil, err
}
return &DBService{db: db}, nil
})
匿名服务,立即加载
type Config struct {
uri string
}
do.ProvideValue[Config](injector, Config{uri: "postgres://user:pass@host:5432/db"})
命名服务,立即加载
do.ProvideNamedValue(injector, "configuration", Config{uri: "postgres://user:pass@host:5432/db"})
服务调用
调用匿名服务
dbService, err := do.Invoke[DBService](injector)
调用匿名服务(如果未注册会panic)
dbService := do.MustInvoke[DBService](injector)
调用命名服务
config, err := do.InvokeNamed[Config](injector, "configuration")
调用命名服务(如果未注册会panic)
config := do.MustInvokeNamed[Config](injector, "configuration")
服务覆盖
do.Provide[Vehicle](injector, func (i *do.Injector) (Vehicle, error) {
return &CarImplem{}, nil
})
do.Override[Vehicle](injector, func (i *do.Injector) (Vehicle, error) {
return &BusImplem{}, nil
})
生命周期钩子
injector := do.NewWithOpts(&do.InjectorOpts{
HookAfterRegistration: func(injector *do.Injector, serviceName string) {
fmt.Printf("Service registered: %s\n", serviceName)
},
HookAfterShutdown: func(injector *do.Injector, serviceName string) {
fmt.Printf("Service stopped: %s\n", serviceName)
},
Logf: func(format string, args ...any) {
log.Printf(format, args...)
},
})
注入器克隆
func TestService(t *testing.T) {
i := injector.Clone()
defer i.Shutdown()
// 用MockService替换Service
do.Override[Service](i, func (i *do.Injector) (Service, error) {
return &MockService{}, nil
}))
app := do.Invoke[*App](i)
// 使用模拟服务进行单元测试
}
总结
do是一个简单而强大的依赖注入框架,特别适合在Go 1.18+项目中使用。它提供了完整的生命周期管理、健康检查和优雅关闭等功能,同时保持了轻量级和类型安全的特点。
更多关于golang基于泛型的依赖注入框架插件do的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang基于泛型的依赖注入框架插件do的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 基于泛型的依赖注入框架 do 使用指南
do
是一个基于 Go 泛型实现的轻量级依赖注入框架,它提供了简单易用的 API 来管理依赖关系。下面我将详细介绍如何使用这个框架。
1. 安装 do 框架
go get github.com/samber/do
2. 基本用法
2.1 创建注入器
package main
import (
"fmt"
"github.com/samber/do"
)
func main() {
injector := do.New()
// 注册服务
do.Provide(injector, func(i *do.Injector) (*ServiceA, error) {
return &ServiceA{}, nil
})
// 获取服务
serviceA, err := do.Invoke[*ServiceA](injector)
if err != nil {
panic(err)
}
serviceA.DoSomething()
}
type ServiceA struct{}
func (s *ServiceA) DoSomething() {
fmt.Println("ServiceA is doing something")
}
2.2 依赖注入
package main
import (
"fmt"
"github.com/samber/do"
)
func main() {
injector := do.New()
// 注册服务B
do.Provide(injector, func(i *do.Injector) (*ServiceB, error) {
return &ServiceB{}, nil
})
// 注册服务A,它依赖服务B
do.Provide(injector, func(i *do.Injector) (*ServiceA, error) {
serviceB, err := do.Invoke[*ServiceB](i)
if err != nil {
return nil, err
}
return &ServiceA{serviceB: serviceB}, nil
})
// 使用服务A
serviceA, err := do.Invoke[*ServiceA](injector)
if err != nil {
panic(err)
}
serviceA.DoSomething()
}
type ServiceA struct {
serviceB *ServiceB
}
func (s *ServiceA) DoSomething() {
fmt.Println("ServiceA is doing something")
s.serviceB.DoSomethingElse()
}
type ServiceB struct{}
func (s *ServiceB) DoSomethingElse() {
fmt.Println("ServiceB is doing something else")
}
3. 高级特性
3.1 命名服务
do.ProvideNamed(injector, "logger", func(i *do.Injector) (*Logger, error) {
return &Logger{}, nil
})
logger, err := do.InvokeNamed[*Logger](injector, "logger")
3.2 单例模式
// 默认情况下,Provide 创建的是单例
do.Provide(injector, func(i *do.Injector) (*Database, error) {
return ConnectToDatabase()
})
// 每次调用都会返回同一个实例
db1, _ := do.Invoke[*Database](injector)
db2, _ := do.Invoke[*Database](injector)
// db1 == db2
3.3 瞬态服务
// 使用 ProvideValue 可以注册非单例服务
do.ProvideValue(injector, &Config{Env: "dev"})
// 或者使用 Provide 但每次都创建新实例
do.Provide(injector, func(i *do.Injector) (*RequestContext, error) {
return &RequestContext{CreatedAt: time.Now()}, nil
})
3.4 服务健康检查
do.Provide(injector, func(i *do.Injector) (*Database, error) {
db := ConnectToDatabase()
do.AddHealthCheck(i, do.HealthCheck{
Name: "database",
Check: func() error {
return db.Ping()
},
})
return db, nil
})
// 检查所有服务健康状态
status := do.HealthCheck(injector)
fmt.Println(status)
4. 生命周期管理
injector := do.New()
// 关闭时会调用所有服务的 Shutdown 方法(如果实现了 Shutdowner 接口)
defer injector.Shutdown()
type Shutdowner interface {
Shutdown() error
}
type ServiceC struct{}
func (s *ServiceC) Shutdown() error {
fmt.Println("ServiceC is shutting down")
return nil
}
do.Provide(injector, func(i *do.Injector) (*ServiceC, error) {
return &ServiceC{}, nil
})
5. 实际应用示例
package main
import (
"fmt"
"github.com/samber/do"
"log"
)
func main() {
// 初始化依赖注入容器
injector := do.New()
defer injector.Shutdown()
// 注册配置
do.ProvideValue(injector, &Config{DatabaseURL: "postgres://user:pass@localhost:5432/db"})
// 注册数据库连接
do.Provide(injector, NewDatabase)
// 注册用户仓库
do.Provide(injector, NewUserRepository)
// 注册用户服务
do.Provide(injector, NewUserService)
// 使用服务
userService, err := do.Invoke[*UserService](injector)
if err != nil {
log.Fatal(err)
}
user, err := userService.GetUser(1)
if err != nil {
log.Fatal(err)
}
fmt.Printf("User: %+v\n", user)
}
// 以下是各个组件的实现
type Config struct {
DatabaseURL string
}
type Database struct {
conn string
}
func NewDatabase(i *do.Injector) (*Database, error) {
cfg := do.MustInvoke[*Config](i)
return &Database{conn: cfg.DatabaseURL}, nil
}
func (db *Database) Query(query string) string {
return fmt.Sprintf("Result of query '%s' from %s", query, db.conn)
}
type UserRepository struct {
db *Database
}
func NewUserRepository(i *do.Injector) (*UserRepository, error) {
db := do.MustInvoke[*Database](i)
return &UserRepository{db: db}, nil
}
func (r *UserRepository) FindByID(id int) string {
return r.db.Query(fmt.Sprintf("SELECT * FROM users WHERE id = %d", id))
}
type UserService struct {
repo *UserRepository
}
func NewUserService(i *do.Injector) (*UserService, error) {
repo := do.MustInvoke[*UserRepository](i)
return &UserService{repo: repo}, nil
}
func (s *UserService) GetUser(id int) (string, error) {
return s.repo.FindByID(id), nil
}
总结
do
框架提供了以下主要优势:
- 基于 Go 泛型,类型安全
- 简单易用的 API
- 支持单例和瞬态服务
- 内置健康检查
- 自动生命周期管理
- 支持命名服务
这个框架非常适合中小型 Go 项目,可以帮助你更好地组织代码和管理依赖关系。对于更复杂的场景,你可能需要考虑其他更全功能的 DI 框架,但对于大多数应用来说,do
已经足够强大了。