高级特性
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
}最佳实践
- 接口优先:使用接口提高灵活性和可测试性
- 分层组织:按架构层次组织 Provider Set
- 清理资源:需要清理的资源返回清理函数
- 避免循环:使用接口解耦避免循环依赖
- 环境分离:不同环境使用不同的 Provider Set
- 模块化:大项目拆分为多个模块