Skip to content

高级特性 ​

Wire 提供多种高级特性满足复杂场景的依赖注入需求。

接口绑定 ​

接口与实现绑定 ​

Wire 支持将接口类型绑定到具体实现。

go
package repo

type UserRepository interface {
    FindByID(id int64) (*User, error)
    Save(user *User) error
}

type MySQLUserRepository struct {
    db *Database
}

func NewMySQLUserRepository(db *Database) *MySQLUserRepository {
    return &MySQLUserRepository{db: db}
}

var RepoSet = wire.NewSet(
    NewMySQLUserRepository,
    wire.Bind(new(UserRepository), new(*MySQLUserRepository)),
)

多接口绑定 ​

go
type Reader interface {
    Read(id int64) (*User, error)
}

type Writer interface {
    Write(user *User) error
}

type Repository struct {
    db *Database
}

var RepoSet = wire.NewSet(
    NewRepository,
    wire.Bind(new(Reader), new(*Repository)),
    wire.Bind(new(Writer), new(*Repository)),
)

结构体注入 ​

wire.Struct ​

使用 wire.Struct 自动填充结构体字段。

go
type App struct {
    Config    *Config
    Database  *Database
    Service   *Service
    Handler   *Handler
}

var AppSet = wire.NewSet(
    NewConfig,
    NewDatabase,
    NewService,
    NewHandler,
    wire.Struct(new(App), "*"),  // 填充所有字段
)

func InitializeApp() *App {
    wire.Build(AppSet)
    return nil
}

选择字段 ​

go
// 只填充指定字段
wire.Struct(new(App), "Config", "Service")

// 排除字段
wire.Struct(new(App), "Config", "Database", "Service")  // 不填充 Handler

值绑定 ​

wire.Value ​

使用固定值作为 Provider。

go
type Config struct {
    Host string
    Port int
}

func InitializeApp() *App {
    wire.Build(
        wire.Value(Config{Host: "localhost", Port: 8080}),
        NewDatabase,
        NewService,
        NewApp,
    )
    return nil
}

wire.InterfaceValue ​

为接口提供固定值。

go
type Logger interface {
    Info(msg string)
    Error(msg string)
}

type StdLogger struct{}

func (l *StdLogger) Info(msg string)  { fmt.Println("INFO:", msg) }
func (l *StdLogger) Error(msg string) { fmt.Println("ERROR:", msg) }

func InitializeApp() *App {
    wire.Build(
        wire.InterfaceValue(new(Logger), &StdLogger{}),
        NewService,
        NewApp,
    )
    return nil
}

Provider Set 组合 ​

组合多个 Set ​

go
var DataSet = wire.NewSet(
    NewDatabase,
    NewRepository,
)

var BizSet = wire.NewSet(
    NewService,
    NewUseCase,
)

var ServerSet = wire.NewSet(
    NewHandler,
    NewServer,
)

var AllSet = wire.NewSet(
    DataSet,
    BizSet,
    ServerSet,
    NewApp,
)

按环境组合 ​

go
var DevSet = wire.NewSet(
    DataSet,
    BizSet,
    ServerSet,
    wire.Value(Config{Env: "dev"}),
)

var ProdSet = wire.NewSet(
    DataSet,
    BizSet,
    ServerSet,
    wire.Value(Config{Env: "prod"}),
)

func InitializeDevApp() *App {
    wire.Build(DevSet, NewApp)
    return nil
}

func InitializeProdApp() *App {
    wire.Build(ProdSet, NewApp)
    return nil
}

清理函数 ​

单个清理函数 ​

go
func NewDatabase(cfg *Config) (*Database, func(), error) {
    db, err := sql.Open("postgres", cfg.DSN)
    if err != nil {
        return nil, nil, err
    }
    
    cleanup := func() {
        db.Close()
    }
    
    return &Database{DB: db}, cleanup, nil
}

多个清理函数 ​

Wire 自动组合所有清理函数。

go
func NewDatabase(cfg *Config) (*Database, func(), error) {
    // ...
}

func NewCache(cfg *Config) (*Cache, func(), error) {
    // ...
}

func InitializeApp() (*App, func(), error) {
    wire.Build(
        NewDatabase,  // 返回清理函数
        NewCache,     // 返回清理函数
        NewService,
        NewApp,
    )
    return nil, nil, nil
}

生成的代码:

go
func InitializeApp() (*App, func(), error) {
    database, cleanup1, err := NewDatabase(config)
    if err != nil {
        return nil, nil, err
    }
    cache, cleanup2, err := NewCache(config)
    if err != nil {
        cleanup1()
        return nil, nil, err
    }
    service := NewService(database, cache)
    app := NewApp(service)
    return app, func() {
        cleanup2()
        cleanup1()
    }, nil
}

多返回值 ​

返回多个组件 ​

go
func InitializeApp() (*App, *Service, error) {
    wire.Build(
        NewConfig,
        NewDatabase,
        NewRepository,
        NewService,
        NewApp,
    )
    return nil, nil, nil
}

在 main.go 中使用 ​

go
func main() {
    app, service, cleanup, err := InitializeApp()
    if err != nil {
        log.Fatal(err)
    }
    defer cleanup()
    
    // 同时使用 app 和 service
    app.Run()
    service.HealthCheck()
}

条件依赖 ​

使用不同实现 ​

go
type Cache interface {
    Get(key string) (string, error)
    Set(key, value string) error
}

type RedisCache struct { client *redis.Client }
type MemCache struct { data map[string]string }

func NewRedisCache(cfg *Config) *RedisCache {
    return &RedisCache{client: redis.NewClient(...)}
}

func NewMemCache() *MemCache {
    return &MemCache{data: make(map[string]string)}
}

var RedisSet = wire.NewSet(
    NewRedisCache,
    wire.Bind(new(Cache), new(*RedisCache)),
)

var MemSet = wire.NewSet(
    NewMemCache,
    wire.Bind(new(Cache), new(*MemCache)),
)

func InitializeRedisApp() *App {
    wire.Build(RedisSet, CommonSet, NewApp)
    return nil
}

func InitializeMemApp() *App {
    wire.Build(MemSet, CommonSet, NewApp)
    return nil
}

循环依赖 ​

解决循环依赖 ​

Wire 不支持循环依赖,需要重构设计。

go
// 错误示例:循环依赖
type A struct { b *B }
type B struct { a *A }

func NewA(b *B) *A { return &A{b: b} }
func NewB(a *A) *B { return &B{a: a} }

// 正确示例:使用接口解耦
type BInterface interface {
    DoSomething()
}

type A struct { b BInterface }
type B struct {}

func NewA(b BInterface) *A { return &A{b: b} }
func NewB() *B { return &B{} }

var Set = wire.NewSet(
    NewB,
    wire.Bind(new(BInterface), new(*B)),
    NewA,
)

可选依赖 ​

使用指针表示可选 ​

go
type Service struct {
    db    *Database    // 必需
    cache *Cache       // 可选
}

func NewService(db *Database, cache *Cache) *Service {
    return &Service{
        db:    db,
        cache: cache,  // 可能为 nil
    }
}

Provider Set 中排除可选依赖 ​

go
// 不包含 cache Provider
var MinimalSet = wire.NewSet(
    NewDatabase,
    NewService,  // cache 为 nil
)

// 包含 cache Provider
var FullSet = wire.NewSet(
    NewDatabase,
    NewCache,
    NewService,  // cache 有值
)

模块化依赖 ​

多模块 Injector ​

go
package module1

var Module1Set = wire.NewSet(
    NewConfig1,
    NewDatabase1,
)

func InitializeModule1() (*Component1, func(), error) {
    wire.Build(Module1Set)
    return nil, nil, nil
}

package module2

var Module2Set = wire.NewSet(
    NewConfig2,
    NewCache2,
)

func InitializeModule2() (*Component2, func(), error) {
    wire.Build(Module2Set)
    return nil, nil, nil
}

组合模块 ​

go
package app

func InitializeApp() (*App, func(), error) {
    wire.Build(
        module1.Module1Set,
        module2.Module2Set,
        NewApp,
    )
    return nil, nil, nil
}

高级配置 ​

使用配置对象 ​

go
type AppConfig struct {
    Database DatabaseConfig
    Cache    CacheConfig
    Server   ServerConfig
}

func NewAppConfig() *AppConfig {
    return &AppConfig{
        Database: DatabaseConfig{...},
        Cache:    CacheConfig{...},
        Server:   ServerConfig{...},
    }
}

func NewDatabase(cfg *DatabaseConfig) (*Database, func(), error) {
    // 使用 DatabaseConfig 而非完整 AppConfig
}

func InitializeApp() (*App, func(), error) {
    wire.Build(
        NewAppConfig,
        wire.FieldsOf(new(*AppConfig), "Database", "Cache", "Server"),
        NewDatabase,
        NewCache,
        NewServer,
        NewApp,
    )
    return nil, nil, nil
}

最佳实践 ​

  1. 接口优先:使用接口提高灵活性和可测试性
  2. 分层组织:按架构层次组织 Provider Set
  3. 清理资源:需要清理的资源返回清理函数
  4. 避免循环:使用接口解耦避免循环依赖
  5. 环境分离:不同环境使用不同的 Provider Set
  6. 模块化:大项目拆分为多个模块

参考:https://github.com/google/wire/blob/main/docs/guide.md