当前位置: 首页 > news >正文

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 里也无法生成(因为无法推导依赖图)

http://www.dtcms.com/a/273855.html

相关文章:

  • 【JavaScript高级】构造函数、原型链与数据处理
  • 3 OneNET-调试器模拟上报数据
  • 深入理解Spring声明式事务的同步管理机制
  • C++ 面向对象 - 对象定义方法汇总
  • MySQL:分析表锁的常见问题
  • Flowable 使用遇到问题
  • Redis Sentinel哨兵集群
  • 碳中和目标下的全球产业链重构:深度解析与未来路径
  • Maui劝退:用windows直接真机调试iOS,无须和Mac配对
  • 单片机显示Unicode字符介绍
  • PDXP、UDP与HDLC协议技术解析:架构、应用与对比研究
  • SpringBoot 拦截器和过滤器的区别
  • 如何高效验证代理IP的可用性与稳定性
  • 瀚高数据库提交数据后,是否需要COMMIT(APP)
  • oracle
  • 从代码学习深度学习 - 针对序列级和词元级应用微调BERT PyTorch版
  • 线程池拒绝策略执行之后的补偿案例
  • Express + @vladmandic/face-api + mySql 实现人脸识别
  • Oracle 数据库 Dblink
  • 【项目经理】实施项目技术问题
  • 5G标准学习笔记14 - CSI--RS概述
  • Telnet远程连接实验(Cisco)
  • 包稳定的Docker 安装方式(CentOS)
  • 前端实现 MD5 加密
  • 【Linux应用】Ubuntu20.04 aarch64开发板一键安装ROS2(清华源)
  • 登录超时问题的排查方法与预防经验分享
  • 【机器学习】机器学习基础
  • 设计模式笔记_结构型_代理模式
  • GPT3/chatGPT/T5/PaLM/LLaMA/GLM主流大语言模型的原理和差异
  • 触觉反馈手套技术是迈向远程机器人系统灵巧操作的关键一步