golang依赖注入容器实现轻量级解耦插件库di的使用

Golang依赖注入容器实现轻量级解耦插件库di的使用

DI简介

DI是一个用于Go编程语言的依赖注入库。依赖注入是一种控制反转形式,可以增加程序的模块化和可扩展性。该库帮助您组织代码库中的职责,并使将低级实现组合成高级行为变得容易,而无需样板代码。

特性

  • 直观的自动装配
  • 接口实现
  • 构造函数注入
  • 可选注入
  • 字段注入
  • 延迟加载
  • 标记
  • 分组
  • 迭代
  • 装饰
  • 清理
  • 容器链/作用域

安装

go get github.com/defval/di

示例用法

下面是一个完整的示例,展示如何使用di库实现依赖注入:

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"

	"github.com/defval/di"
)

// 定义Controller接口
type Controller interface {
	RegisterRoutes(mux *http.ServeMux)
}

// OrderController实现
type OrderController struct{}

func NewOrderController() *OrderController {
	return &OrderController{}
}

func (c *OrderController) RegisterRoutes(mux *http.ServeMux) {
	mux.HandleFunc("/orders", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Orders page")
	})
}

// UserController实现
type UserController struct{}

func NewUserController() *UserController {
	return &UserController{}
}

func (c *UserController) RegisterRoutes(mux *http.ServeMux) {
	mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Users page")
	})
}

// 创建应用上下文
func NewContext() context.Context {
	return context.Background()
}

// 创建HTTP服务器
func NewServer(mux *http.ServeMux) *http.Server {
	return &http.Server{
		Addr:    ":8080",
		Handler: mux,
	}
}

// 创建ServeMux并注册所有控制器路由
func NewServeMux(controllers []Controller) *http.ServeMux {
	mux := http.NewServeMux()
	for _, controller := range controllers {
		controller.RegisterRoutes(mux)
	}
	return mux
}

// 启动服务器
func StartServer(server *http.Server, ctx context.Context) {
	// 启动服务器
	go func() {
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %s\n", err)
		}
	}()

	// 等待中断信号
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	log.Println("Shutting down server...")

	// 优雅关闭
	if err := server.Shutdown(ctx); err != nil {
		log.Fatal("Server forced to shutdown:", err)
	}
	log.Println("Server exiting")
}

func main() {
	di.SetTracer(&di.StdTracer{})
	// 创建容器
	c, err := di.New(
		di.Provide(NewContext),  // 提供应用上下文
		di.Provide(NewServer),   // 提供HTTP服务器
		di.Provide(NewServeMux), // 提供HTTP serve mux
		// 控制器作为[]Controller组
		di.Provide(NewOrderController, di.As(new(Controller))),
		di.Provide(NewUserController, di.As(new(Controller))),
	)
	// 处理容器错误
	if err != nil {
		log.Fatal(err)
	}
	// 调用函数
	if err := c.Invoke(StartServer); err != nil {
		log.Fatal(err)
	}
}

代码说明

  1. 容器创建:使用di.New()创建容器,并通过di.Provide()注册各种依赖项
  2. 接口绑定:使用di.As()将具体实现绑定到接口
  3. 依赖注入:容器会自动解析并注入依赖关系
  4. 函数调用:使用c.Invoke()调用需要依赖注入的函数

这个示例展示了如何:

  • 定义接口和实现
  • 使用DI容器管理依赖
  • 自动装配HTTP服务器和控制器
  • 实现优雅的服务器启动和关闭

通过这种方式,您可以轻松地添加新的控制器或修改现有实现,而无需修改大量代码,实现了组件间的松耦合。


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

1 回复

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


Golang依赖注入容器实现轻量级解耦插件库

在Go语言中实现依赖注入(Dependency Injection, DI)可以帮助我们构建松耦合、易于测试和维护的应用程序。下面我将介绍一个轻量级的依赖注入容器实现,并展示如何使用它来解耦插件库。

基本概念

依赖注入是一种设计模式,它将对象的创建和绑定从使用它们的代码中分离出来。主要优点包括:

  • 降低组件间的耦合度
  • 便于单元测试
  • 提高代码的可维护性和可扩展性

轻量级DI容器实现

以下是一个简单的依赖注入容器实现:

package di

import (
	"reflect"
	"sync"
)

// Container 依赖注入容器
type Container struct {
	components sync.Map
}

// NewContainer 创建新的容器实例
func NewContainer() *Container {
	return &Container{}
}

// Provide 向容器中注册组件
func (c *Container) Provide(name string, component interface{}) {
	c.components.Store(name, component)
}

// Resolve 从容器中获取组件
func (c *Container) Resolve(name string) interface{} {
	val, ok := c.components.Load(name)
	if !ok {
		panic("component not found: " + name)
	}
	return val
}

// Invoke 调用函数并自动注入依赖
func (c *Container) Invoke(fn interface{}) {
	fnType := reflect.TypeOf(fn)
	if fnType.Kind() != reflect.Func {
		panic("fn must be a function")
	}

	// 准备函数参数
	numIn := fnType.NumIn()
	args := make([]reflect.Value, numIn)
	for i := 0; i < numIn; i++ {
		argType := fnType.In(i)
		var found interface{}
		
		// 遍历容器查找匹配类型的组件
		c.components.Range(func(key, value interface{}) bool {
			if reflect.TypeOf(value).AssignableTo(argType) {
				found = value
				return false // 停止遍历
			}
			return true
		})
		
		if found == nil {
			panic("dependency not found for type: " + argType.String())
		}
		args[i] = reflect.ValueOf(found)
	}

	// 调用函数
	fnValue := reflect.ValueOf(fn)
	fnValue.Call(args)
}

使用示例

下面展示如何使用这个DI容器来解耦插件系统:

package main

import (
	"fmt"
	"di"
)

// 定义插件接口
type Plugin interface {
	Execute() string
}

// 插件A实现
type PluginA struct{}

func (p *PluginA) Execute() string {
	return "PluginA executed"
}

// 插件B实现
type PluginB struct{}

func (p *PluginB) Execute() string {
	return "PluginB executed"
}

// 插件管理器
type PluginManager struct {
	plugins []Plugin
}

func NewPluginManager(plugins ...Plugin) *PluginManager {
	return &PluginManager{plugins: plugins}
}

func (pm *PluginManager) RunAll() {
	for _, p := range pm.plugins {
		fmt.Println(p.Execute())
	}
}

func main() {
	// 创建DI容器
	container := di.NewContainer()
	
	// 注册组件
	container.Provide("pluginA", &PluginA{})
	container.Provide("pluginB", &PluginB{})
	
	// 使用依赖注入创建插件管理器
	var pm *PluginManager
	container.Invoke(func(p1 Plugin, p2 Plugin) {
		pm = NewPluginManager(p1, p2)
	})
	
	// 运行插件
	pm.RunAll()
	
	// 输出:
	// PluginA executed
	// PluginB executed
}

高级用法

1. 单例模式

// 确保组件只被创建一次
var once sync.Once
var singleton *SomeService

container.Provide("service", func() interface{} {
	once.Do(func() {
		singleton = &SomeService{}
	})
	return singleton
})

2. 构造函数注入

type DatabaseConfig struct {
	DSN string
}

type Database struct {
	config *DatabaseConfig
}

container.Provide("config", &DatabaseConfig{DSN: "user:pass@tcp(localhost:3306)/db"})
container.Provide("db", func(c *DatabaseConfig) *Database {
	return &Database{config: c}
})

3. 接口绑定

type Logger interface {
	Log(msg string)
}

type FileLogger struct{}

func (f *FileLogger) Log(msg string) {
	fmt.Println("FileLogger:", msg)
}

container.Provide("logger", new(FileLogger))

// 使用时可以接收Logger接口
container.Invoke(func(l Logger) {
	l.Log("test message")
})

与第三方库对比

虽然我们实现了一个简单的DI容器,但在生产环境中,你可能需要考虑使用更成熟的库,如:

  1. uber/fx - Uber提供的依赖注入框架
  2. google/wire - Google提供的编译时依赖注入
  3. samber/do - 简单轻量级的DI容器

总结

通过实现和使用轻量级的依赖注入容器,我们可以:

  1. 解耦组件间的直接依赖
  2. 提高代码的可测试性
  3. 简化组件的管理和配置
  4. 支持灵活的插件架构

这种模式特别适合中大型项目或需要高度可扩展性的插件系统。根据项目复杂度,你可以选择自行实现简单的DI容器或使用成熟的第三方库。

回到顶部