前言
本文通过代码示例,
详细的讲述了在Golang中如何通过
goole/wire来进行项目整体的依赖注入管理的管理和维护
,
通过wire为我们的项目依赖树有一个统一维护的地方,全局统一管理依赖。
wire 最大的价值 正是在复杂项目里,把依赖关系集中在一个地方(通常是 wire.go),做到:
全局统一管理依赖
编译期安全(不像 fx 在运行期才报错)
避免到处写构造 & 注入逻辑,让模块更专注业务
wire 管理目录的方式
其实就是目录放在哪里合适:
是整个项目都放在一个文件内管理呢,还是在各自的模块创建wire来分开管理,我感觉这个看个人或者团队的规范习惯吧。
全局统一那就一个目录一个文件统一管理,放在各自的功能模块下=>那就每个模块自己独立管理,大同小异
全局一个文件(感觉这样比较方便,只用运行一次wire 命令,生成全局文件就行)(我的简易的代码示例):
//go:build wireinject
// +build wireinject
package provider
import (
"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 dao
import "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 service
import "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 controller
import (
"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 wireinject
package user
import (
"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, // 注入 dao
service.NewUserService, // 注入 service
controller.NewUserController, // 注入 controller
)
return &controller.UserController{}
}
解释:
wire.Build(...)
告诉 wire:有这 3 个构造函数需要调用,自动分析依赖关系wire 根据参数
dbConn string
推导出先调用 NewUserDAO,再往上传递
5️⃣ cmd/main.go
package main
import (
"example-wire/internal/user"
)
func main() {
// 假设数据库连接
dbConn := "mysql://localhost:3306"
// wire 生成 InitializeUserController,返回最终 controller
controller := 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 里也无法生成(因为无法推导依赖图)