Golang Samber Do

Polar Sponsor
爱发电 赞助
.NET 9.0

使用 samber/do 在 Golang 中实现依赖注入 — 服务容器、生命周期管理、作用域、健康检查、优雅关闭和模块组织

Persona: 您是Go 建筑师, 设置依赖性注射

功能概述

Persona: 您是Go 建筑师, 设置依赖性注射是一项面向实际任务的技能,主要用于您将容器保存在组成根, 依赖于接口而非混凝土类型, 并将提供者错误作为首键处理。它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。

核心要点

  • 使用时应结合输入条件选择合适的执行方式,核对必要参数、依赖环境与输出内容,并按原始要求处理异常情况。
  • 该技能适合需要稳定复用相关能力的场景,可作为自动化工作流的一部分,也便于后续检查、调整和扩展。
  • 从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。

使用与执行

实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。

结果检查与注意事项

执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。

角色:你是一位 Go 架构师,正在搭建依赖注入系统。你将容器保留在组合根(composition root)处,依赖接口而非具体类型,并将提供者(provider)错误视为一等公民级的失败。

在 Go 中使用 samber/do 实现依赖注入

基于 Go 1.18+ 泛型构建的类型安全依赖注入工具包。

官方资源:

  • pkg.go.dev/github.com/samber/do/v2
  • do.samber.dev
  • github.com/samber/do/v2

本技能介绍并不详尽。请参考该库的文档与代码示例获取更多信息。Context7 可作为可发现性平台提供辅助。

请勿使用该库的 v1 版本,请安装 v2 版本:

go get -u github.com/samber/do/v2

核心概念

注入器(容器)

import "github.com/samber/do/v2"

injector := do.New()

服务类型

  • Lazy(默认):首次请求时创建
  • Eager:容器启动时立即创建
  • Transient:每次请求均创建新实例
  • Value:预创建的值,无需实例化

提供者函数

服务必须通过提供者函数注册:

type Provider[T any] func(i Injector) (T, error)

基础用法

1. 定义并注册服务

遵循“接受接口,返回结构体”原则:

// 注册服务(默认为 lazy)
do.Provide(injector, func(i do.Injector) (Database, error) {
    return &PostgreSQLDatabase{connString: "postgres://..."}, nil
})

// 注册预创建的值
do.ProvideValue(injector, &Config{Port: 8080})

// 注册 transient 服务(每次调用均新建实例)
do.ProvideTransient(injector, func(i do.Injector) (*Logger, error) {
    return &Logger{}, nil
})

// 注册 eager 服务(启动时立即创建)
do.ProvideValue(injector, &Config{Port: 8080})

2. 获取服务

容器仅应在组合根处被访问:

// 带错误处理地获取服务
db, err := do.Invoke[Database](injector)

// MustInvoke 在出错时 panic(适用于确信服务一定存在的情形)
db := do.MustInvoke[Database](injector)

3. 服务依赖

func NewUserService(i do.Injector) (UserService, error) {
    db := do.MustInvoke[Database](i)
    cache := do.MustInvoke[Cache](i)
    return &userService{db: db, cache: cache}, nil
}

do.Provide(injector, NewUserService)

4. 隐式别名(推荐方式)

注册具体类型后,可直接以接口类型获取,无需显式别名:

// 注册具体类型
do.Provide(injector, func(i do.Injector) (*PostgreSQLDatabase, error) {
    return &PostgreSQLDatabase{}, nil
})

// 直接以接口类型获取(隐式别名)
db := do.MustInvokeAs[Database](injector)

5. 命名服务

注册同一类型的多个服务:

do.ProvideNamed(injector, "primary-db", func(i do.Injector) (*Database, error) {
    return &Database{URL: "postgres://primary..."}, nil
})

mainDB := do.MustInvokeNamed[*Database](injector, "primary-db")

包组织方式

使用 do.Package() 按模块组织服务注册:

// infrastructure/package.go
var Package = do.Package(
    do.Lazy(func(i do.Injector) (*postgres.DB, error) {
        cfg := do.MustInvoke[*Config](i)
        return postgres.Connect(cfg.DatabaseURL)
    }),
    do.Lazy(func(i do.Injector) (*redis.Client, error) {
        cfg := do.MustInvoke[*Config](i)
        return redis.NewClient(cfg.RedisURL), nil
    }),
)

// main.go
injector := do.New(infrastructure.Package, service.Package)

完整应用初始化

func main() {
    injector := do.New(
        infrastructure.Package,
        repository.Package,
        service.Package,
        transport.Package,
    )

    server := do.MustInvoke[*http.Server](injector)
    go server.ListenAndServe()

    _ = injector.ShutdownOnSignalsWithContext(context.Background(), os.Interrupt)
}

最佳实践

  1. 依赖接口而非具体类型——这使你能在测试中替换实现,而无需修改生产代码
  2. 每个服务应只承担单一职责——职责过多的服务更难测试、也更难替换
  3. 保持依赖树扁平——超过 3–4 层的依赖链会使初始化顺序变得脆弱,且错误更难追踪
  4. 在提供者函数中妥善处理错误——静默失败的提供者会导致服务处于损坏状态,进而在意想不到的位置引发崩溃
  5. 使用作用域(scopes)按生命周期组织服务——请求作用域(request-scoped)服务可防止内存泄漏,全局作用域(global)服务可避免重复初始化

关于作用域、生命周期管理、结构体注入及调试,请参阅《高级用法》。

关于测试模式(克隆、覆盖、模拟),请参阅《测试》。

速查表

注册

函数 用途
do.Provide[T]() 注册 lazy 服务(默认)
do.ProvideNamed[T]() 注册命名的 lazy 服务
do.ProvideValue[T]() 注册预创建的值
do.ProvideNamedValue[T]() 注册命名的值
do.ProvideTransient[T]() 每次调用均创建新实例
do.ProvideNamedTransient[T]() 注册命名的 transient 服务
do.Package() 对服务注册进行分组

获取

函数 用途
do.Invoke[T]() 获取服务(带错误返回)
do.InvokeNamed[T]() 获取命名服务
do.InvokeAs[T]() 获取首个匹配该接口的服务
do.InvokeStruct[T]() 通过 struct tag 向结构体字段注入依赖
do.MustInvoke[T]() 获取服务(出错则 panic)
do.MustInvokeNamed[T]() 获取命名服务(出错则 panic)
do.MustInvokeAs[T]() 按接口类型获取服务(出错则 panic)
do.MustInvokeStruct[T]() 向结构体注入依赖(出错则 panic)

交叉引用

  • → 查看 samber/cc-skills-golang@golang-dependency-injection 技能,了解 DI 核心概念、方案对比以及何时引入 DI 库
  • → 查看 samber/cc-skills-golang@golang-structs-interfaces 技能,了解接口设计模式
  • → 查看 samber/cc-skills-golang@golang-testing 技能,了解通用测试模式

相关专题

更多
GORM框架数据模型设计教程
GORM框架数据模型设计教程

本专题讲解GORM框架数据模型设计方法,涵盖模型定义、字段标签、主键设置、自定义类型、Hook钩子、表名映射、字段映射与自动迁移等内容,帮助开发者掌握GORM框架模型设计技巧,实现数据库结构规范管理。

2026.08.13

300

16

GORM框架数据库操作详解
GORM框架数据库操作详解

本专题围绕GORM框架数据库操作展开,涵盖增删改查、CRUD操作、条件查询、分页排序、批量处理、事务操作及多数据库应用等内容,帮助开发者掌握GORM框架数据操作方法,提升Go语言数据库开发效率。

2026.08.13

200

13

GORM框架安装配置指南
GORM框架安装配置指南

本专题汇总GORM框架安装配置指南,涵盖GORM安装配置、环境搭建、数据库连接、多数据库支持、读写分离、连接池优化、事务处理及项目应用实践,帮助开发者掌握Go语言ORM框架使用方法,实现高效稳定的数据访问与管理。

2026.08.13

260

17

Kubernetes运维优化教程
Kubernetes运维优化教程

本专题总结Kubernetes运维优化实战经验,覆盖性能监控、指标告警、内存泄漏定位、网络延迟排查与成本优化,并演示Go语言性能剖析与调优手段,帮助运维开发协同提升集群与应用的稳定性和资源效率。

2026.08.13

140

14

Kubernetes云原生开发教程
Kubernetes云原生开发教程

本专题围绕Kubernetes云原生开发展开,涵盖Go语言集成、Docker部署、集群管理、微服务架构、client-go开发及CI/CD实践等内容,结合项目案例帮助开发者掌握容器编排、云原生应用开发与Kubernetes企业级实践能力。

2026.08.13

180

14

Kubernetes安装部署教程
Kubernetes安装部署教程

本专题系统整理Kubernetes安装部署全流程,涵盖kubeadm集群搭建、Golang环境配置、二进制部署、网络插件与存储对接,结合Go语言实战演示节点初始化与验证方法,帮助开发者从零搭建稳定可用的Kubernetes集群环境。

2026.08.13

160

14

Go语言微服务熔断降级与限流系统设计
Go语言微服务熔断降级与限流系统设计

本专题聚焦 Go 语言在微服务稳定性建设中的核心技术,讲解熔断机制、限流算法、服务降级策略以及分布式系统保护设计方法。通过实际架构案例,帮助开发者构建具备高可用与自我保护能力的后端服务系统。

2026.06.22

87

18

Go微服务与gRPC高性能通信实战
Go微服务与gRPC高性能通信实战

本专题围绕 Go 语言在微服务架构下的高性能通信实践展开,深入讲解 gRPC 协议、服务注册与发现、负载均衡、拦截器与性能调优策略。通过实战示例,帮助开发者构建高效可靠的分布式后端服务系统。

2026.04.27

718

17

Golang网络编程与高并发服务设计实践
Golang网络编程与高并发服务设计实践

本专题聚焦 Go 语言在高并发网络服务开发中的应用,讲解 TCP/HTTP 协议处理、Goroutine 调度、Channel 并发通信以及服务性能调优策略。通过实践案例,帮助开发者构建高性能、稳定可靠的后端服务系统。

2026.04.13

59

22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程