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)
}
}
代码说明
- 容器创建:使用
di.New()
创建容器,并通过di.Provide()
注册各种依赖项 - 接口绑定:使用
di.As()
将具体实现绑定到接口 - 依赖注入:容器会自动解析并注入依赖关系
- 函数调用:使用
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容器,但在生产环境中,你可能需要考虑使用更成熟的库,如:
- uber/fx - Uber提供的依赖注入框架
- google/wire - Google提供的编译时依赖注入
- samber/do - 简单轻量级的DI容器
总结
通过实现和使用轻量级的依赖注入容器,我们可以:
- 解耦组件间的直接依赖
- 提高代码的可测试性
- 简化组件的管理和配置
- 支持灵活的插件架构
这种模式特别适合中大型项目或需要高度可扩展性的插件系统。根据项目复杂度,你可以选择自行实现简单的DI容器或使用成熟的第三方库。