Wire 基本概念
理解 Wire 的核心概念是使用依赖注入的基础。
依赖注入概述
什么是依赖注入
依赖注入(Dependency Injection, DI)是一种设计模式,将依赖关系的创建和管理从代码中分离出来。
go
// 不使用依赖注入
type Service struct {
db *Database
}
func NewService() *Service {
db := NewDatabase() // Service 内部创建 Database
return &Service{db: db}
}
// 使用依赖注入
func NewService(db *Database) *Service {
return &Service{db: db} // Database 由外部传入
}依赖注入的优势
| 优势 | 说明 |
|---|---|
| 解耦 | 组件不依赖具体实现 |
| 可测试 | 易于替换依赖进行测试 |
| 可维护 | 依赖关系清晰可见 |
| 可扩展 | 易于添加新功能 |
Wire 解决的问题
go
// 手动组装依赖
func main() {
db := NewDatabase()
repo := NewRepository(db)
service := NewService(repo)
handler := NewHandler(service)
app := &App{handler: handler}
app.Run()
}
// Wire 自动组装依赖
func main() {
app, cleanup, err := InitializeApp()
if err != nil {
log.Fatal(err)
}
defer cleanup()
app.Run()
}Provider
Provider 定义
Provider 是创建依赖组件的函数,Wire 使用这些函数构建依赖图。
go
// Provider 函数
func NewDatabase() *Database {
return &Database{
Host: "localhost",
Port: 5432,
}
}
func NewRepository(db *Database) *Repository {
return &Repository{db: db}
}
func NewService(repo *Repository) *Service {
return &Service{repo: repo}
}Provider 类型
简单 Provider
go
func NewLogger() *Logger {
return &Logger{
Level: "info",
}
}带 Provider Set
go
var DatabaseSet = wire.NewSet(
NewDatabase,
NewRepository,
)接口绑定
go
type Repository interface {
Find(id int) (*User, error)
Save(user *User) error
}
type MySQLRepository struct {
db *Database
}
func NewMySQLRepository(db *Database) *MySQLRepository {
return &MySQLRepository{db: db}
}
// 接口绑定 Provider
var RepositorySet = wire.NewSet(
NewMySQLRepository,
wire.Bind(new(Repository), new(*MySQLRepository)),
)返回错误
go
func NewDatabase(cfg *Config) (*Database, error) {
if cfg.Host == "" {
return nil, errors.New("host is required")
}
return &Database{Host: cfg.Host}, nil
}返回清理函数
go
func NewDatabase(cfg *Config) (*Database, func(), error) {
db, err := sql.Open("mysql", cfg.ConnectionString)
if err != nil {
return nil, nil, err
}
cleanup := func() {
db.Close()
}
return &Database{DB: db}, cleanup, nil
}Injector
Injector 定义
Injector 是声明依赖组装方式的函数,Wire 根据 Injector 生成代码。
go
// wire.go
//+build wireinject
package main
import "github.com/google/wire"
func InitializeApp() (*App, error) {
wire.Build(
NewConfig,
NewDatabase,
NewRepository,
NewService,
NewApp,
)
return nil, nil
}Injector 规则
- 返回值:Injector 返回组装后的结果
- wire.Build:使用 wire.Build 声明所有 Provider
- 空返回:Injector 函数体只包含 wire.Build,返回 nil
- 构建标签:使用
//+build wireinject避免编译冲突
Injector 返回类型
go
// 返回单个值
func InitializeApp() *App {
wire.Build(...)
return nil
}
// 返回值和错误
func InitializeApp() (*App, error) {
wire.Build(...)
return nil, nil
}
// 返回值、清理函数和错误
func InitializeApp() (*App, func(), error) {
wire.Build(...)
return nil, nil, nil
}
// 返回多个值
func InitializeApp() (*App, *Service, error) {
wire.Build(...)
return nil, nil, nil
}Provider Set
Provider Set 定义
Provider Set 是一组 Provider 的集合,用于组织相关依赖。
go
// 数据层 Provider Set
var DataSet = wire.NewSet(
NewDatabase,
NewRepository,
wire.Bind(new(Repository), new(*MySQLRepository)),
)
// 业务层 Provider Set
var BizSet = wire.NewSet(
NewService,
NewUseCase,
)
// 服务层 Provider Set
var ServiceSet = wire.NewSet(
NewHandler,
NewServer,
)组合 Provider Set
go
var AllSet = wire.NewSet(
DataSet,
BizSet,
ServiceSet,
NewApp,
)
func InitializeApp() (*App, error) {
wire.Build(AllSet)
return nil, nil
}依赖图
依赖图概念
Wire 分析 Provider 之间的依赖关系,构建依赖图。
App
└─ Handler
└─ Service
└─ Repository
└─ Database
└─ Config依赖图示例
go
// Provider 定义依赖图
func NewConfig() *Config // 无依赖
func NewDatabase(cfg *Config) *Database // 依赖 Config
func NewRepository(db *Database) Repository // 依赖 Database
func NewService(repo Repository) *Service // 依赖 Repository
func NewHandler(svc *Service) *Handler // 依赖 Service
func NewApp(h *Handler) *App // 依赖 Handler
// Wire 自动构建依赖图
func InitializeApp() (*App, error) {
wire.Build(
NewConfig,
NewDatabase,
NewRepository,
NewService,
NewHandler,
NewApp,
)
return nil, nil
}生成代码
go
// wire_gen.go (自动生成)
func InitializeApp() (*App, error) {
config := NewConfig()
database, err := NewDatabase(config)
if err != nil {
return nil, err
}
repository := NewRepository(database)
service := NewService(repository)
handler := NewHandler(service)
app := NewApp(handler)
return app, nil
}结构体 Provider
结构体作为 Provider
go
type App struct {
Handler *Handler
Service *Service
}
func NewApp(handler *Handler, service *Service) *App {
return &App{
Handler: handler,
Service: service,
}
}
// 使用 wire.Struct 自动填充
func InitializeApp() *App {
wire.Build(
NewHandler,
NewService,
wire.Struct(new(App), "Handler", "Service"),
)
return nil
}wire.Struct 选项
go
// 填充所有字段
wire.Struct(new(App), "*")
// 填充指定字段
wire.Struct(new(App), "Handler", "Service")
// 排除字段
wire.Struct(new(App), "Handler") // 只填充 Handler值 Provider
使用 wire.Value
go
func InitializeApp() *App {
wire.Build(
wire.Value(Config{Host: "localhost"}),
NewDatabase,
NewApp,
)
return nil
}使用 wire.InterfaceValue
go
type Logger interface {
Info(msg string)
}
func InitializeApp() *App {
wire.Build(
wire.InterfaceValue(new(Logger), &DefaultLogger{}),
NewApp,
)
return nil
}最佳实践
- 分层组织:按架构层次组织 Provider Set
- 接口绑定:使用接口提高灵活性
- 错误处理:Provider 返回错误处理初始化失败
- 清理函数:资源需要清理时返回清理函数
- 命名清晰:Provider 函数使用 NewXXX 命名
- 分离文件:将 wire.go 和业务代码分离