本文通过一个完整 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 基本就通了。