Go中使用wire进行统一依赖注入管理

发布于:2025-07-12 ⋅ 阅读:(16) ⋅ 点赞:(0)

在这里插入图片描述

前言

本文通过代码示例,
详细的讲述了在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 里也无法生成(因为无法推导依赖图)


网站公告

今日签到

点亮在社区的每一天
去签到