Wire 是 Google 提供的一个用于生成依赖注入代码的工具。它通过分析代码中的提供者(providers)函数和结构体构造函数,自动生成依赖注入的代码,从而减少手动编写依赖管理代码的负担。

下面是一个使用 Wire 的简单示例。

示例场景

假设我们有一个简单的应用程序,它有一个 UserService,该服务依赖于 UserRepositoryUserRepository 又依赖于一个 Database 连接。

目录结构

1
2
3
4
5
6
- main.go
- wire.go
- wire_gen.go
- database.go
- user_repository.go
- user_service.go

1. 定义依赖项

database.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

type Database struct {
DSN string
}

func NewDatabase() *Database {
return &Database{
DSN: "user:password@tcp(127.0.0.1:3306)/dbname",
}
}

func (db *Database) Connect() {
fmt.Println("Connecting to database with DSN:", db.DSN)
}

user_repository.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

type UserRepository struct {
db *Database
}

func NewUserRepository(db *Database) *UserRepository {
return &UserRepository{db: db}
}

func (r *UserRepository) FindUser(id int) {
r.db.Connect()
// Logic to find a user in the database
}

user_service.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

type UserService struct {
repo *UserRepository
}

func NewUserService(repo *UserRepository) *UserService {
return &UserService{repo: repo}
}

func (s *UserService) GetUser(id int) {
s.repo.FindUser(id)
// Logic to return user data
}

2. 使用 Wire 进行依赖注入

wire.go

在这个文件中,我们定义 Wire 的依赖注入配置。

1
2
3
4
5
6
7
8
9
10
11
//go:build wireinject
// +build wireinject

package main

import "github.com/google/wire"

func InitializeUserService() *UserService {
wire.Build(NewDatabase, NewUserRepository, NewUserService)
return &UserService{}
}

3. 生成依赖注入代码

运行 wire 命令生成依赖注入代码:

1
2
3
4
5
go install github.com/google/wire/cmd/wire@latest
wire

# 或者
go run -mod=mod github.com/google/wire/cmd/wire

这个命令会生成一个名为 wire_gen.go 的文件,其中包含了实际的依赖注入代码。

wire_gen.go

生成的代码会类似于以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Code generated by Wire. DO NOT EDIT.

//go:generate go run github.com/google/wire/cmd/wire
// +build !wireinject

package main

// Injectors from wire.go:

func InitializeUserService() *UserService {
database := NewDatabase()
userRepository := NewUserRepository(database)
userService := NewUserService(userRepository)
return userService
}

4. 使用依赖注入的服务

main.go

1
2
3
4
5
6
package main

func main() {
userService := InitializeUserService()
userService.GetUser(1)
}

总结

在这个示例中,Wire 自动生成了依赖注入代码,帮助我们初始化了 UserService 及其依赖项。Wire 的强大之处在于它能够在项目变大时自动管理复杂的依赖关系,并确保构造函数被正确调用。

go:build wireinject 标签

//go:build wireinject// +build wireinject 是 Go 的 build 标签,用来控制特定文件的编译行为。这两个标签主要用于在使用 Google 的 Wire 工具进行依赖注入时,区分生成代码和手写代码。

作用解释

  1. //go:build wireinject:
    • 这是 Go 1.17 及更高版本使用的新的 build 标签语法。
    • 它指示 Go 编译器,只有在显式指定 wireinject 标签时,才会编译这个文件。
  2. // +build wireinject:
    • 这是 Go 1.16 及之前版本使用的老式 build 标签语法。
    • 同样,它也指示 Go 编译器,只有在显式指定 wireinject 标签时,才会编译这个文件。

用途

Wire 生成代码的场景中,这两个标签的主要作用是:

  • 隔离手动编写的依赖注入代码wire.go 文件中通常包含 Wire 的依赖注入设置和生成器函数。在实际生产环境中,你不会希望这个文件被编译进最终的二进制文件中,因为 wire.go 只是用于生成代码的,而不需要在运行时使用。
  • 生成依赖注入代码:运行 wire 命令时,Wire 工具会读取 wire.go 文件中的依赖注入配置,然后生成相应的代码文件(例如 wire_gen.go),这个文件中不包含 wireinject 标签。
  • 防止重复编译:因为 wire.go 文件使用了 wireinject 标签,所以在没有指定 wireinject 标签的情况下(即普通编译流程),该文件不会被编译进应用程序。这避免了编译错误和重复代码的问题。

例子

wire.go 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
go
复制代码
//go:build wireinject
// +build wireinject

package main

import "github.com/google/wire"

func InitializeUserService() *UserService {
wire.Build(NewDatabase, NewUserRepository, NewUserService)
return &UserService{}
}

在正常的编译过程中,wire.go 文件将被忽略。只有在运行 wire 命令时,该文件会被读取并用来生成依赖注入代码。

生成代码

wire 工具生成的代码文件,例如 wire_gen.go,则没有这些标签,确保它们在普通的编译流程中被正常编译和使用。

总结

//go:build wireinject// +build wireinject 标签的主要作用是控制文件的编译条件,确保 wire.go 文件只在特定条件下(例如生成依赖注入代码时)参与编译,而不会影响最终的生产代码编译流程。这使得代码更整洁,同时也防止了不必要的编译和运行时错误。