golang,go,博客,开源,编程

golang每日一库之依赖注入库samber/do

Updated on with 0 views and 0 comments

do 是 Go 语言中一个轻量级的依赖注入(Dependency Injection, DI)容器,由 samber 开发。

它基于 Go 1.18+ 泛型实现,为 Go 提供了一个类型安全的 DI 方案。

do 库的设计理念是简化服务组件之间的依赖管理,取代手工创建依赖关系的繁琐工作,使不同组件之间松散耦合、更易测试与维护。

与反射型 DI 框架不同,do 在注册和解析依赖时不使用反射,因此性能开销很小。

功能简介

  • 服务注册:使用 do.Provide 系列函数将服务构造函数注册到容器中(默认懒加载,即按需单例创建);也可以使用 ProvideTransient 注册每次调用都新建实例的工厂(瞬时模式);或使用 ProvideValue/ProvideNamedValue 注册已经创建好的实例(急加载)。注册时可指定名称或匿名服务(推荐匿名,由框架自动命名)。
  • 依赖解析:通过 do.Invoke[T](injector)do.MustInvoke[T](injector) 获取指定类型的服务实例。容器会自动根据函数签名的参数解析依赖,并以依赖图的方式按顺序实例化各服务(默认单例)。服务加载顺序为调用顺序(先调用的服务会优先初始化)。
  • 生命周期管理do 支持生命周期钩子。服务只要实现特定接口,就会被框架在适当时机调用。比如实现 do.Healthcheckable 接口的服务可以通过 do.HealthCheck[T](injector)injector.HealthCheck() 进行健康检查;实现 do.Shutdownable 接口的服务会在容器关闭时被回调,以便释放资源。容器关闭时会按照服务注册的 反初始化顺序(后注册的先关闭)依次调用这些 Shutdown 方法。
  • 其它特性:支持服务命名、覆盖(Override*)和组合(do.Package),可以复制容器(injector.Clone()),并提供工具函数列出已注册或已实例化的服务列表。整个库非常轻量,无外部依赖,也无需生成代码。

参考示例

下面给出一个简单示例,演示如何使用 do 完成依赖注入。假设有两个服务 EngineCar,其中 Car 依赖于 Engine

type Engine struct {
    Started bool
}
// 构造函数:创建 Engine 实例
func NewEngine(i do.Injector) (*Engine, error) {
    return &Engine{Started: false}, nil
}

type Car struct {
    Engine *Engine
}
// 构造函数:创建 Car 实例时自动注入 Engine
func NewCar(i do.Injector) (*Car, error) {
    engine := do.MustInvoke[*Engine](i) // 从容器中获取 Engine
    return &Car{Engine: engine}, nil
}

func (c *Car) Start() {
    c.Engine.Started = true
    println("Car is running")
}

main 函数中创建容器、注册服务并调用:

func main() {
    injector := do.New()                // 创建依赖注入容器
    do.Provide(injector, NewEngine)    // 注册 Engine 服务(懒加载单例)
    do.Provide(injector, NewCar)       // 注册 Car 服务,依赖 Engine

    // 获取 Car 实例(会自动创建 Car 及其依赖 Engine)
    car := do.MustInvoke[*Car](injector)
    car.Start()                         // 使用服务

    injector.Shutdown()                // 关闭容器,按注册顺序倒序调用可选的 Shutdown 钩子
}

以上代码中,调用 do.Provide 将构造函数注册到容器,do.MustInvoke[*Car] 会触发容器根据依赖关系创建对象并返回。运行后会输出 "Car is running"。如果服务实现了 Shutdown() 方法(实现 do.Shutdownable),容器在调用 injector.Shutdown() 时也会自动调用它完成清理。

优势有哪些

使用 do 库相比手动通过 new 或自己维护全局变量的方式,有以下优势:

  • 松耦合与易测试:通过容器集中管理依赖关系,组件之间无需硬编码调用构造函数,降低耦合度,更易替换和模拟测试。依赖关系一目了然,也方便单元测试时注入假组件。
  • 类型安全do 完全基于 Go 泛型和静态类型检查,不使用反射,不会产生运行时类型错误。编译期就能发现依赖缺失或类型不匹配的问题,避免了很多隐蔽错误。
  • 代码简洁:简化了大量样板代码,只需通过构造函数和少量注册代码,框架就自动解决依赖,减少手工传参和变量管理的工作,提高开发效率。
  • 生命周期管理:容器能统一处理启动、健康检查、优雅停机等流程,无需在业务代码中编写额外逻辑,适合需要集中管理服务生命周期的应用场景。
  • 轻量无依赖do 自身很轻量,没有外部依赖和代码生成工具,运行时开销低,适合小型服务、命令行工具、微服务等多种场景。

对于有多个互相依赖组件、希望简化启动与测试流程的项目,do 是一个友好的选择。


标题:golang每日一库之依赖注入库samber/do
作者:mooncakeee
地址:http://blog.dd95828.com/articles/2025/05/09/1746754797718.html
联系:scotttu@163.com