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,默认为false
  • OTEL_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

1 回复

更多关于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))

最佳实践

  1. 面向接口编程:总是依赖接口而非具体实现
  2. 单一职责:每个组件应该只做一件事
  3. 合理使用生命周期:根据需求选择适当的生命周期
  4. 避免服务定位器模式:尽量使用构造函数注入而非直接获取服务

Kod是一个简单而强大的依赖注入框架,它利用Go的泛型特性提供了类型安全的DI实现。通过合理使用Kod,你可以构建出松耦合、易于测试和维护的应用程序。

回到顶部