Go中使用wire进行统一依赖注入管理
前言
本文通过代码示例,
详细的讲述了在Golang中如何通过
goole/wire来进行项目整体的依赖注入管理的管理和维护
,
通过wire为我们的项目依赖树有一个统一维护的地方,全局统一管理依赖。
wire 最大的价值 正是在复杂项目里,把依赖关系集中在一个地方(通常是 wire.go),做到:
全局统一管理依赖
编译期安全(不像 fx 在运行期才报错)
避免到处写构造 & 注入逻辑,让模块更专注业务
wire 管理目录的方式
其实就是目录放在哪里合适:
是整个项目都放在一个文件内管理呢,还是在各自的模块创建wire来分开管理,我感觉这个看个人或者团队的规范习惯吧。
全局统一那就一个目录一个文件统一管理,放在各自的功能模块下=>那就每个模块自己独立管理,大同小异
全局一个文件(感觉这样比较方便,只用运行一次wire 命令,生成全局文件就行)(我的简易的代码示例):
//go:build wireinject
// +build wireinjectpackage providerimport ("client-system/app/controller/meeting_controller""client-system/app/controller/meeting_participant_controller""client-system/app/controller/user_controller""client-system/app/dao/meeting_dao""client-system/app/dao/meeting_participant_dao""client-system/app/dao/user_dao""client-system/app/service/meeting_participant_service""client-system/app/service/meeting_service""client-system/app/service/user_service""client-system/app/websocket/connection"websocket_service "client-system/app/websocket/service""client-system/pkg/utils""github.com/google/wire"
)// UserControllerSet 用户控制器依赖集合
var UserControllerSet = wire.NewSet(user_dao.NewDao,user_service.NewService,user_controller.NewController,
)// MeetingServiceSet 会议服务依赖集合
var MeetingServiceSet = wire.NewSet(user_dao.NewDao,meeting_dao.NewDao,meeting_participant_dao.NewDao,meeting_participant_service.NewService,meeting_service.NewService,
)// MeetingControllerSet 会议控制器依赖集合
var MeetingControllerSet = wire.NewSet(user_dao.NewDao,meeting_dao.NewDao,meeting_participant_dao.NewDao,meeting_participant_service.NewService,meeting_service.NewService,meeting_controller.NewController,
)// WebSocketServiceSet WebSocket服务集合
var WebSocketServiceSet = wire.NewSet(user_dao.NewDao,meeting_dao.NewDao,meeting_participant_dao.NewDao,meeting_participant_service.NewService,user_service.NewService,meeting_service.NewService,connection.ProvideConnectionManager,utils.ProvideWorkerPool,websocket_service.NewWebSocketService,
)// MeetingParticipantServiceSet 会议参与者服务集合
var MeetingParticipantServiceSet = wire.NewSet(user_dao.NewDao,meeting_participant_dao.NewDao,meeting_participant_service.NewService,meeting_participant_controller.NewController,
)// InitializeUserController 自动生成用户控制器构造函数
func InitializeUserController() (*user_controller.Controller, error) {wire.Build(UserControllerSet)return nil, nil
}// InitializeMeetingService 自动生成会议服务构造函数
func InitializeMeetingService() (*meeting_service.Service, error) {wire.Build(MeetingServiceSet)return nil, nil
}// InitializeMeetingController 自动生成会议控制器构造函数
func InitializeMeetingController() (*meeting_controller.Controller, error) {wire.Build(MeetingControllerSet)return nil, nil
}// InitializeMeetingParticipantController 自动生成会议参与者控制器构造函数
func InitializeMeetingParticipantController() (*meeting_participant_controller.Controller, error) {wire.Build(MeetingParticipantServiceSet)return nil, nil
}// InitializeWebSocketService 自动生成WebSocket服务构造函数
func InitializeWebSocketService() (*websocket_service.WebSocketService, error) {wire.Build(WebSocketServiceSet)return nil, nil
}
各模块自己管理,那就把上面的拆开,放在各自模块下(但是生成依赖文件的时候需要在每个模块的相关wire文件所在的目录运行 wire 命令)
下面我们来实现一个 完整的、详细注释 的 wire 示例,场景:
- user 模块:controller(handler) → service → dao(repository)
- 使用 Google Wire 做依赖注入管理。
✅ 项目结构(简化版)
example-wire/
├── cmd/
│ └── main.go
├── internal/
│ └── user/
│ ├── controller/
│ │ └── user_controller.go
│ ├── service/
│ │ └── user_service.go
│ ├── dao/
│ │ └── user_dao.go
│ └── wire.go
📦 示例详细代码
1️⃣ dao/user_dao.go
package daoimport "fmt"// UserDAO 定义数据访问对象
type UserDAO struct {dbConn string
}// NewUserDAO 构造函数,需要数据库连接
func NewUserDAO(dbConn string) *UserDAO {return &UserDAO{dbConn: dbConn}
}func (d *UserDAO) GetUser(id int) string {return fmt.Sprintf("User%d from DB (%s)", id, d.dbConn)
}
2️⃣ service/user_service.go
package serviceimport "example-wire/internal/user/dao"// UserService 定义业务层
type UserService struct {dao *dao.UserDAO
}// NewUserService 构造函数注入 DAO
func NewUserService(dao *dao.UserDAO) *UserService {return &UserService{dao: dao}
}func (s *UserService) GetUserInfo(id int) string {return s.dao.GetUser(id)
}
3️⃣ controller/user_controller.go
package controllerimport ("fmt""example-wire/internal/user/service"
)// UserController 控制器层
type UserController struct {service *service.UserService
}// NewUserController 构造函数注入 service
func NewUserController(s *service.UserService) *UserController {return &UserController{service: s}
}func (c *UserController) GetUserHandler(id int) {user := c.service.GetUserInfo(id)fmt.Println("Controller get user:", user)
}
4️⃣ internal/user/wire.go
这是 wire 配置文件,核心!
//go:build wireinject
// +build wireinjectpackage userimport ("github.com/google/wire""example-wire/internal/user/controller""example-wire/internal/user/service""example-wire/internal/user/dao"
)// InitializeUserController 是 wire 注入入口
func InitializeUserController(dbConn string) *controller.UserController {wire.Build(dao.NewUserDAO, // 注入 daoservice.NewUserService, // 注入 servicecontroller.NewUserController, // 注入 controller)return &controller.UserController{}
}
解释:
wire.Build(...)
告诉 wire:有这 3 个构造函数需要调用,自动分析依赖关系wire 根据参数
dbConn string
推导出先调用 NewUserDAO,再往上传递
5️⃣ cmd/main.go
package mainimport ("example-wire/internal/user"
)func main() {// 假设数据库连接dbConn := "mysql://localhost:3306"// wire 生成 InitializeUserController,返回最终 controllercontroller := user.InitializeUserController(dbConn)// 测试调用controller.GetUserHandler(1)
}
✏️ 6️⃣ 使用 wire 生成代码
第一次需要安装 wire:
go install github.com/google/wire/cmd/wire@latest
在 internal/user 目录下运行(就是在我们定义的wire.go的目录内):
wire
会生成一个 wire_gen.go
文件,里面自动生成了:
调用 NewUserDAO → NewUserService → NewUserController 的代码
确保编译期依赖关系正确
✅ 总结
wire 的好处:
全局只写一次依赖关系(wire.go)
编译期安全,防止遗漏
模块里只关心业务,不关心怎么 new
项目大了更方便管理
注意:
wire 只做初始化,不建议全项目到处 wire
建议只在根层(main / internal/user)写 wire 文件
wire 本身不适合运行期动态注入,需要动态注入就用 fx/dig
✅ 遗留问题:
wire跨模块依赖引用、循环依赖的问题
(将在下一篇文章 如何在go项目中使用wire处理跨模块依赖引用、循环依赖的问题,点击即可跳转到文章)
🌱 正常引用:没问题
user service 需要调用 order dao → wire 可以正常组装,只要你写好构造函数
甚至 user service 想同时用 user dao + order dao,也没问题
⚠️ 循环依赖:会出问题
如果 user service 需要调用 order service,而 order service 又需要调用 user service
或者 user dao → order dao → user dao
这种就构成了循环依赖(我们在下篇文章来具体说明,该如何正确处理这类问题):
在 Go 里也无法编译通过(编译时会说:import cycle)
在 wire 里也无法生成(因为无法推导依赖图)