golang轻量级IoC依赖注入容器插件库GoLobby/Container的使用

Golang轻量级IoC依赖注入容器插件库GoLobby/Container的使用

GoLobby Container是一个轻量级但功能强大的Go项目IoC(依赖注入)容器。它设计简洁、易于使用,并注重性能。

主要特性

  • 单例(Singleton)和瞬时(Transient)绑定
  • 命名依赖项(绑定)
  • 通过函数、变量和结构体解析依赖
  • 将错误转换为panic的Must帮助方法
  • 可选的延迟加载绑定
  • 小型应用的全局实例
  • 100%测试覆盖率

安装

在项目目录中运行以下命令安装:

go get github.com/golobby/container/v3

然后在代码中引入:

import "github.com/golobby/container/v3"

快速入门

基本绑定和解析示例

// 将Config接口绑定到JsonConfig结构体
err := container.Singleton(func() Config {
    return &JsonConfig{...}
})

var c Config
err := container.Resolve(&c)
// `c` 将是JsonConfig的实例

绑定类型

单例绑定(Singleton)

err := container.Singleton(func() Abstraction {
  return Implementation
})

// 如果需要返回错误
err := container.Singleton(func() (Abstraction, error) {
  return Implementation, nil
})

示例:

err := container.Singleton(func() Database {
  return &MySQL{}
})

瞬时绑定(Transient)

err := container.Transient(func() Shape {
  return &Rectangle{}
})

命名绑定

可以为同一个抽象类型绑定多个具体实现:

// 单例命名绑定
err := container.NamedSingleton("square", func() Shape {
    return &Rectangle{}
})
err := container.NamedSingleton("rounded", func() Shape {
    return &Circle{}
})

// 瞬时命名绑定
err := container.NamedTransient("sql", func() Database {
    return &MySQL{}
})
err := container.NamedTransient("noSql", func() Database {
    return &MongoDB{}
})

解析依赖

使用引用解析

var a Abstraction
err := container.Resolve(&a)
// `a` 将是Abstraction的实现

命名解析示例:

var s Shape
err := container.NamedResolve(&s, "rounded")
// `s` 将是名为"rounded"的Shape实现

使用闭包解析

err := container.Call(func(db Database) {
  // `db` 将是Database接口的实现
  db.Query("...")
})

可以解析多个依赖:

err := container.Call(func(db Database, s Shape) {
  db.Query("...")
  s.Area()
})

使用结构体解析

type App struct {
    mailer Mailer   `container:"type"`
    sql    Database `container:"name"`
    noSql  Database `container:"name"`
    other  int
}

myApp := App{}

err := container.Fill(&myApp)

// `myApp.mailer` 将是Mailer接口的实现
// `myApp.sql` 将是名为"sql"的Database实现
// `myApp.noSql` 将是名为"noSql"的Database实现
// `myApp.other` 将被忽略,因为它没有container标签

绑定时解析依赖

可以在绑定时解析其他依赖:

// 将Config绑定到JsonConfig
err := container.Singleton(func() Config {
    return &JsonConfig{...}
})

// 将Database绑定到MySQL
err := container.Singleton(func(c Config) Database {
    // `c` 将是JsonConfig的实例
    return &MySQL{
        Username: c.Get("DB_USERNAME"),
        Password: c.Get("DB_PASSWORD"),
    }
})

独立实例

默认情况下,容器将绑定保存在全局实例中。你也可以创建独立实例:

c := container.New()

err := c.Singleton(func() Database {
    return &MySQL{}
})

err := c.Call(func(db Database) {
    db.Query("...")
})

Must帮助方法

这些方法在出错时会panic而不是返回错误:

c := container.New()

container.MustSingleton(c, func() Shape {
    return &Circle{a: 13}
})

container.MustCall(c, func(s Shape) {
    // ...
})

延迟绑定

延迟绑定会推迟调用解析函数,直到第一次请求:

// 延迟单例绑定
err := container.SingletonLazy(func() Database {
    return &MySQL{}
})

// 延迟命名绑定
err := container.NamedSingletonLazy("db", func() Database {
    return &PostgreSQL{}
})

性能提示

容器不可避免地使用反射进行绑定和解析过程。如果性能是关键考虑因素,请尝试在只运行一次的地方(如main或init函数)绑定和解析依赖。

完整示例

package main

import (
	"fmt"
	"github.com/golobby/container/v3"
)

// 定义接口和实现
type Database interface {
	Query(string) string
}

type MySQL struct{}

func (m MySQL) Query(q string) string {
	return fmt.Sprintf("MySQL查询结果: %s", q)
}

type PostgreSQL struct{}

func (p PostgreSQL) Query(q string) string {
	return fmt.Sprintf("PostgreSQL查询结果: %s", q)
}

func main() {
	// 绑定接口到具体实现
	err := container.Singleton(func() Database {
		return &MySQL{}
	})
	if err != nil {
		panic(err)
	}

	// 命名绑定
	err = container.NamedSingleton("postgres", func() Database {
		return &PostgreSQL{}
	})
	if err != nil {
		panic(err)
	}

	// 解析默认实现
	var db Database
	err = container.Resolve(&db)
	if err != nil {
		panic(err)
	}
	fmt.Println(db.Query("SELECT * FROM users"))

	// 解析命名实现
	var pgDb Database
	err = container.NamedResolve(&pgDb, "postgres")
	if err != nil {
		panic(err)
	}
	fmt.Println(pgDb.Query("SELECT * FROM products"))

	// 使用闭包解析
	err = container.Call(func(db Database) {
		fmt.Println(db.Query("SELECT * FROM orders"))
	})
	if err != nil {
		panic(err)
	}
}

这个示例展示了GoLobby Container的基本用法,包括单例绑定、命名绑定以及多种解析方式。


更多关于golang轻量级IoC依赖注入容器插件库GoLobby/Container的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang轻量级IoC依赖注入容器插件库GoLobby/Container的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


GoLobby/Container - 轻量级Go依赖注入容器使用指南

GoLobby/Container是一个轻量级的依赖注入(DI)容器,专为Go语言设计。它简单易用,适合中小型项目实现依赖注入。

安装

go get github.com/golobby/container/v3

基本用法

1. 绑定与解析

package main

import (
	"fmt"
	"github.com/golobby/container/v3"
)

type Database interface {
	Connect() string
}

type MySQL struct{}

func (m MySQL) Connect() string {
	return "Connected to MySQL"
}

type Postgres struct{}

func (p Postgres) Connect() string {
	return "Connected to Postgres"
}

func main() {
	// 绑定接口到具体实现
	err := container.Singleton(func() Database {
		return MySQL{} // 可以轻松切换为Postgres{}
	})
	if err != nil {
		panic(err)
	}

	// 解析依赖
	var db Database
	err = container.Resolve(&db)
	if err != nil {
		panic(err)
	}

	fmt.Println(db.Connect()) // 输出: Connected to MySQL
}

2. 单例与瞬态绑定

// 单例模式(整个应用生命周期内只有一个实例)
container.Singleton(func() Database {
	return MySQL{}
})

// 瞬态模式(每次解析都会创建新实例)
container.Transient(func() Database {
	return MySQL{}
})

3. 命名绑定

// 绑定命名实例
container.NamedSingleton("mysql", func() Database {
	return MySQL{}
})

container.NamedSingleton("postgres", func() Database {
	return Postgres{}
})

// 解析命名实例
var db Database
container.NamedResolve(&db, "mysql")

4. 构造函数参数注入

type UserService struct {
	db Database
}

func main() {
	// 绑定依赖
	container.Singleton(func() Database {
		return MySQL{}
	})

	// 绑定UserService,自动注入Database
	container.Transient(func(db Database) UserService {
		return UserService{db: db}
	})

	// 解析UserService
	var service UserService
	container.Resolve(&service)
	
	fmt.Println(service.db.Connect())
}

高级特性

1. 容器嵌套

// 创建子容器
childContainer := container.New()

// 在子容器中绑定
childContainer.Singleton(func() Database {
	return Postgres{}
})

// 使用子容器解析
var db Database
childContainer.Resolve(&db)

2. 条件绑定

// 根据环境变量决定使用哪种数据库
container.Singleton(func() Database {
	if os.Getenv("DB_TYPE") == "postgres" {
		return Postgres{}
	}
	return MySQL{}
})

3. 接口绑定验证

var _ Database = (*MySQL)(nil) // 确保MySQL实现了Database接口

实际应用示例

package main

import (
	"fmt"
	"github.com/golobby/container/v3"
	"log"
)

// 定义接口
type Logger interface {
	Log(message string)
}

type FileLogger struct{}

func (f FileLogger) Log(message string) {
	fmt.Printf("Log to file: %s\n", message)
}

type UserRepository interface {
	GetUser(id int) string
}

type UserRepo struct {
	logger Logger
}

func (r UserRepo) GetUser(id int) string {
	r.logger.Log(fmt.Sprintf("Getting user %d", id))
	return fmt.Sprintf("User%d", id)
}

type UserService struct {
	repo UserRepository
}

func (s UserService) GetUserName(id int) string {
	return s.repo.GetUser(id)
}

func main() {
	// 设置依赖
	err := container.Singleton(func() Logger {
		return FileLogger{}
	})
	if err != nil {
		log.Fatal(err)
	}

	err = container.Singleton(func(l Logger) UserRepository {
		return UserRepo{logger: l}
	})
	if err != nil {
		log.Fatal(err)
	}

	err = container.Singleton(func(r UserRepository) UserService {
		return UserService{repo: r}
	})
	if err != nil {
		log.Fatal(err)
	}

	// 解析并使用
	var service UserService
	err = container.Resolve(&service)
	if err != nil {
		log.Fatal(err)
	}

	name := service.GetUserName(123)
	fmt.Println("User name:", name)
}

最佳实践

  1. 接口优先设计:尽可能依赖接口而不是具体实现
  2. 单一职责:每个服务/仓库应只关注一件事
  3. 避免服务定位器模式:直接注入所需依赖,而不是从容器中获取
  4. 合理使用生命周期
    • 单例:适用于无状态服务、配置、数据库连接池
    • 瞬态:适用于有状态或需要隔离的服务

与其他库对比

GoLobby/Container相比其他DI容器(如dig、fx)的优势:

  • 更轻量级,API更简单
  • 无反射,性能更好
  • 更符合Go的简洁哲学

缺点:

  • 功能相对较少
  • 缺少一些高级特性如装饰器、作用域生命周期

总结

GoLobby/Container是一个简单实用的依赖注入容器,适合需要轻量级DI解决方案的Go项目。它通过简洁的API实现了依赖注入的核心功能,帮助开发者编写更松耦合、更易测试的代码。

回到顶部