golang基于泛型的依赖注入框架插件库kod的使用
Golang基于泛型的依赖注入框架插件库Kod的使用
Kod(全称Killer Of Dependency)是一个基于泛型的Go语言依赖注入框架,采用组件化设计思想构建应用程序。
核心特性
- 组件化:Kod是一个基于组件的框架,组件是Kod应用的构建块
- 可配置:可以使用TOML/YAML/JSON文件配置应用程序运行方式
- 测试支持:提供Test函数用于测试Kod应用
- 日志:提供日志API
kod.L
,并与部署环境集成 - OpenTelemetry:依赖OpenTelemetry收集应用的追踪和指标
- 钩子:提供组件启动和停止时运行代码的方式
- 拦截器:内置常见拦截器,组件可注入这些拦截器到方法中
- 接口生成:提供从结构体生成接口的方式
- 代码生成:为Kod应用生成相关代码
安装
go install github.com/go-kod/kod/cmd/kod@latest
安装成功后,运行kod -h
查看帮助信息。
快速入门
创建组件
组件是Kod的核心抽象。组件由Go接口和对应的实现组成。例如:
// Adder定义组件接口
type Adder interface {
Add(context.Context, int, int) (int, error)
}
// adder定义组件实现
type adder struct {
kod.Implements[Adder] // 嵌入kod.Implements[T]字段,T是实现的接口
}
// Add实现Adder接口
func (*adder) Add(_ context.Context, x, y int) (int, error) {
return x + y, nil
}
主程序
package main
import (
"context"
"fmt"
"log"
"github.com/go-kod/kod"
)
func main() {
if err := kod.Run(context.Background(), serve); err != nil {
log.Fatal(err)
}
}
// app是应用的主组件
type app struct{
kod.Implements[kod.Main]
}
// serve由kod.Run调用,包含应用主体
func serve(context.Context, *app) error {
fmt.Println("Hello")
return nil
}
运行程序:
go mod tidy
kod generate .
go run .
配置
Kod使用TOML格式的配置文件。最小配置只需指定应用名称:
[kod]
name = "hello"
组件配置
可以为组件添加特定配置。首先定义配置结构体:
type greeterOptions struct {
Greeting string `toml:"my_custom_name"`
}
然后在组件实现中嵌入kod.WithConfig[T]
:
type greeter struct {
kod.Implements[Greeter]
kod.WithConfig[greeterOptions]
}
在配置文件中添加对应配置:
["example.com/mypkg/Greeter"]
Greeting = "Bonjour"
全局配置
也可以使用kod.WithGlobalConfig
读取整个配置文件:
type greeter struct {
kod.Implements[Greeter]
kod.WithGlobalConfig[greeterOptions]
}
测试
单元测试
package main
import (
"context"
"testing"
"github.com/go-kod/kod"
)
func TestAdd(t *testing.T) {
kod.RunTest(t, func(ctx context.Context, adder Adder) {
got, err := adder.Add(ctx, 1, 2)
if err != nil {
t.Fatal(err)
}
if want := 3; got != want {
t.Fatalf("got %q, want %q", got, want)
}
})
}
基准测试
func BenchmarkAdd(b *testing.B) {
kod.RunTest(b, func(ctx context.Context, adder Adder) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := adder.Add(ctx, 1, 2)
if err != nil {
b.Fatal(err)
}
}
})
}
模拟测试
可以替换组件实现为模拟实现:
// fakeClock是Clock组件的模拟实现
type fakeClock struct {
now int64
}
// Now实现Clock组件接口
func (f *fakeClock) Now(context.Context) (int64, error) {
return f.now, nil
}
func TestClock(t *testing.T) {
t.Run("fake", func(t *testing.T) {
// 注册模拟实现
fake := kod.Fake[Clock](&fakeClock{100})
kod.RunTest(t, func(ctx context.Context, clock Clock) {
now, err := clock.UnixMicro(ctx)
if err != nil {
t.Fatal(err)
}
if now != 100 {
t.Fatalf("bad time: got %d, want %d", now, 100)
}
fake.now = 200
now, err = clock.UnixMicro(ctx)
if err != nil {
t.Fatal(err)
}
if now != 200 {
t.Fatalf("bad time: got %d, want %d", now, 200)
}
}, kod.WithFakes(fake))
})
}
日志
Kod提供日志API kod.L
:
type Adder interface {
Add(context.Context, int, int) (int, error)
}
type adder struct {
kod.Implements[Adder]
}
func (a *adder) Add(ctx context.Context, x, y int) (int, error) {
// adder嵌入kod.Implements[Adder]提供L方法
logger := a.L(ctx)
logger.DebugContext(ctx, "A debug log.")
logger.InfoContext(ctx, "An info log.")
logger.ErrorContext(ctx, "An error log.", fmt.Errorf("an error"))
return x + y, nil
}
OpenTelemetry
Kod依赖OpenTelemetry收集追踪和指标。支持的环境变量:
OTEL_SDK_DISABLED
:设为true禁用OpenTelemetry SDK,默认为falseOTEL_LOGS_EXPORTER
:日志导出器,支持"console"和"otlp",默认为"otlp"OTEL_EXPORTER_OTLP_PROTOCOL
:OTLP导出器协议,支持"grpc"和"http/protobuf",默认为"http/protobuf"OTEL_EXPORTER_OTLP_INSECURE
:设为true禁用OTLP导出器的安全特性,默认为false
完整示例
package main
import (
"context"
"fmt"
"log"
"github.com/go-kod/kod"
)
func main() {
if err := kod.Run(context.Background(), serve); err != nil {
log.Fatal(err)
}
}
// App组件
type App struct {
kod.Implements[kod.Main]
adder kod.Ref[Adder] // 依赖Adder组件
}
// Adder接口
type Adder interface {
Add(context.Context, int, int) (int, error)
}
// adder实现
type adder struct {
kod.Implements[Adder]
}
func (a *adder) Add(ctx context.Context, x, y int) (int, error) {
logger := a.L(ctx)
logger.InfoContext(ctx, "Adding numbers", "x", x, "y", y)
return x + y, nil
}
// serve函数
func serve(ctx context.Context, app *App) error {
sum, err := app.adder.Get().Add(ctx, 1, 2)
if err != nil {
return err
}
fmt.Println("Sum:", sum)
return nil
}
这个示例展示了Kod的核心功能:组件定义、依赖注入、日志记录等。通过kod.Ref[T]
可以获取组件引用,Kod会自动处理依赖关系。
更多关于golang基于泛型的依赖注入框架插件库kod的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang基于泛型的依赖注入框架插件库kod的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang基于泛型的依赖注入框架Kod使用指南
Kod是一个基于Go泛型的轻量级依赖注入(DI)框架,它提供了简单易用的API来实现依赖注入功能。下面我将详细介绍Kod的使用方法。
安装Kod
首先安装Kod库:
go get github.com/go-kod/kod
基本概念
Kod的核心概念包括:
- 组件(Component):需要被注入或提供注入的对象
- 接口(Interface):定义组件的行为
- 实现(Implementation):接口的具体实现
- 容器(Container):管理组件生命周期的容器
基本用法
1. 定义接口和实现
package main
import "context"
// 定义服务接口
type Greeter interface {
Greet(ctx context.Context, name string) (string, error)
}
// 实现接口
type greeterImpl struct {
// 可以注入其他依赖
}
func (g *greeterImpl) Greet(ctx context.Context, name string) (string, error) {
return "Hello, " + name, nil
}
2. 注册组件
package main
import (
"github.com/go-kod/kod"
)
func main() {
// 创建Kod容器
k := kod.New()
// 注册Greeter接口的实现
kod.Register[Greeter](k, func() (Greeter, error) {
return &greeterImpl{}, nil
})
// 或者使用更简洁的方式
kod.MustRegister[Greeter](k, &greeterImpl{})
}
3. 获取并使用组件
func main() {
// ... 上面的注册代码
// 获取Greeter实例
g, err := kod.Get[Greeter](k)
if err != nil {
panic(err)
}
// 使用Greeter
msg, err := g.Greet(context.Background(), "World")
if err != nil {
panic(err)
}
println(msg) // 输出: Hello, World
}
依赖注入
Kod的强大之处在于它能自动处理依赖关系:
type Logger interface {
Log(ctx context.Context, msg string)
}
type loggerImpl struct{}
func (l *loggerImpl) Log(ctx context.Context, msg string) {
println(msg)
}
// 修改greeterImpl,使其依赖Logger
type greeterImpl struct {
logger Logger // 依赖Logger
}
func (g *greeterImpl) Greet(ctx context.Context, name string) (string, error) {
g.logger.Log(ctx, "Greeting "+name)
return "Hello, " + name, nil
}
func main() {
k := kod.New()
// 注册Logger
kod.MustRegister[Logger](k, &loggerImpl{})
// 注册Greeter,Kod会自动注入Logger
kod.MustRegister[Greeter](k, &greeterImpl{})
// 使用Greeter
g, _ := kod.Get[Greeter](k)
msg, _ := g.Greet(context.Background(), "World")
println(msg)
}
生命周期管理
Kod支持不同的生命周期:
// 单例模式(默认)
kod.MustRegister[Greeter](k, &greeterImpl{}, kod.WithSingleton())
// 每次请求新实例
kod.MustRegister[Greeter](k, &greeterImpl{}, kod.WithTransient())
// 作用域生命周期(如每个HTTP请求一个实例)
kod.MustRegister[Greeter](k, &greeterImpl{}, kod.WithScoped())
高级用法
1. 命名注册
kod.MustRegister[Greeter](k, &englishGreeter{}, kod.WithName("english"))
kod.MustRegister[Greeter](k, &chineseGreeter{}, kod.WithName("chinese"))
// 获取指定名称的实现
g, _ := kod.GetNamed[Greeter](k, "chinese")
2. 选项模式
type GreeterOptions struct {
Prefix string
}
type greeterImpl struct {
prefix string
}
func NewGreeter(opts GreeterOptions) *greeterImpl {
return &greeterImpl{prefix: opts.Prefix}
}
func main() {
k := kod.New()
kod.MustRegister[Greeter](k,
func() (Greeter, error) {
return NewGreeter(GreeterOptions{Prefix: "Hi, "}), nil
},
)
}
3. 拦截器
// 定义拦截器
func loggingInterceptor(ctx context.Context, method string, args []interface{}, next func(context.Context, []interface{}) ([]interface{}, error)) ([]interface{}, error) {
fmt.Printf("Calling %s with args %v\n", method, args)
return next(ctx, args)
}
// 注册带拦截器的组件
kod.MustRegister[Greeter](k, &greeterImpl{}, kod.WithInterceptors(loggingInterceptor))
最佳实践
- 面向接口编程:总是依赖接口而非具体实现
- 单一职责:每个组件应该只做一件事
- 合理使用生命周期:根据需求选择适当的生命周期
- 避免服务定位器模式:尽量使用构造函数注入而非直接获取服务
Kod是一个简单而强大的依赖注入框架,它利用Go的泛型特性提供了类型安全的DI实现。通过合理使用Kod,你可以构建出松耦合、易于测试和维护的应用程序。