Karp 的技术博客
本文通过一个完整 Demo,系统整理 Go Wire 的“依次 / 链式依赖注入”机制
上一个 Provider 的 return 值,如何自动作为下一个 Provider 的参数。

一、什么是 Wire 的「依次依赖注入」

一句话概括:

Wire 会根据“返回值类型 → 参数类型”的匹配关系,自动把多个构造函数串成一条调用链,并在编译期生成代码。

它不是:

  • 运行时容器
  • 反射注入
  • 黑盒魔法

而是:

  • 构建依赖图(Dependency Graph)
  • 排序依赖
  • 生成明确的 Go 构造代码

二、一个最典型的依赖链

假设我们有如下对象层级:

App
 └── UserService
      └── UserRepo
           └── *sql.DB

对应的构造函数:

func NewDB() *sql.DB

func NewUserRepo(db *sql.DB) *UserRepo

func NewUserService(repo *UserRepo) *UserService

func NewApp(svc *UserService) *App

这里已经完整声明了“依次依赖关系”

  • NewDB 返回 *sql.DB
  • NewUserRepo 需要 *sql.DB
  • NewUserService 需要 *UserRepo
  • NewApp 需要 *UserService

三、Wire Build:只声明,不传参

初始化函数(通常写在 wire.go):

//go:build wireinject
// +build wireinject

func InitializeApp() *App {
    wire.Build(
        NewDB,
        NewUserRepo,
        NewUserService,
        NewApp,
    )
    return nil
}

注意:

  • 没有参数传递
  • 没有顺序要求
  • 只声明「有哪些 Provider」

四、Wire 生成的代码(关键)

运行:

wire

生成代码(简化后):

func InitializeApp() *App {
    db := NewDB()
    userRepo := NewUserRepo(db)
    userService := NewUserService(userRepo)
    app := NewApp(userService)
    return app
}

👉 这就是 “依次依赖注入” 的真实形态

上一个 Provider 的 return,自动作为下一个 Provider 的参数

五、依次注入的 4 条核心规则

1️⃣ 只看类型,不看顺序

wire.Build(
    NewUserService,
    NewApp,
    NewDB,
    NewUserRepo,
)

即使顺序是乱的:

  • Wire 也会先建依赖图
  • 再自动排序

2️⃣ 一个返回值,可以被多个依赖复用

func NewDB() *sql.DB

func NewUserRepo(db *sql.DB) *UserRepo
func NewOrderRepo(db *sql.DB) *OrderRepo

生成代码:

db := NewDB()
userRepo := NewUserRepo(db)
orderRepo := NewOrderRepo(db)

3️⃣ 多返回值(error)也能参与链式注入

func NewDB() (*sql.DB, error)

生成代码:

db, err := NewDB()
if err != nil {
    return nil, err
}
repo := NewUserRepo(db)

Wire 会:

  • 自动处理 error
  • 中断后续依赖构造

4️⃣ interface 注入,本质仍是 return → param

type UserRepo interface {
    Find(id int) string
}

func NewMySQLUserRepo(db *sql.DB) *MySQLUserRepo
var RepoSet = wire.NewSet(
    NewMySQLUserRepo,
    wire.Bind(new(UserRepo), new(*MySQLUserRepo)),
)

真实链路是:

*sql.DB
  ↓
*MySQLUserRepo
  ↓ (Bind)
UserRepo

Bind 不创建对象,只做类型映射


六、Wire 的本质模型(重要)

Wire 在编译期做了三件事:

  1. 收集 Provider 的:输入类型 & 输出类型
  2. 构建依赖有向图(DAG)
  3. 按拓扑顺序生成构造代码

所以它能:

  • 检测缺失依赖
  • 检测循环依赖
  • 保证构造顺序 100% 正确

七、为什么说这是“很 Go 的 DI”

因为:

  • 没有反射
  • 没有运行时容器
  • 没有隐式魔法
  • 生成的代码你完全可以手写

Wire 只是:

把“你本来就该写的构造代码”,按类型规则,自动帮你写出来,并保证写得对。

八、结语

Wire 的依次依赖注入,并不是概念,而是明确的代码生成规则:

Provider 的 return 值 → 按类型 → 作为下一个 Provider 的参数 → 串成完整构造链

理解这一点,Wire 基本就通了。

golang

版权属于:karp
作品采用:本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
更新于: 2026年01月23日 07:02
0

目录

来自 《# Go Wire 依次依赖注入原理与 Demo》