Gone V2 Provider 机制介绍

项目地址:github.com/gone-io/gon...

原文地址:Gone V2 Provider 机制介绍

Gone V2 Provider 机制介绍

Gone V2 版本完全基于 Provider 机制实现依赖注入,其核心思想是通过 Provider 为对象提供依赖,而不必将所有第三方对象都包装为 Goner。下面将从 Gone 的依赖注入流程、Provider 的分类及其使用示例等方面进行详细说明。


1. Gone 的依赖注入流程

Gone 框架的依赖注入过程主要分为以下三个步骤:

  1. 标记需要注入的对象

    对象需嵌入 gone.Flag,并对需要注入的字段使用 gone 标签。例如:

    go 复制代码
    type UseConf struct {
     gone.Flag
     Name string `gone:"config,name"`
     Int  string `gone:"config",int`
     Dep  *Dep   `gone:"*,extend"`
    }
    • gone 标签格式:gone:"${name},${extend}"
      • ${name}:指定 Provider 的名字或目标 Goner 的名字(当为 * 或省略时,表示按类型注入)。
      • ${extend}:扩展参数,将传递给 Provider 的 Provide 方法(可选)。
  2. 注册对象

    通过 Load(goner Goner, options ...Option) 将需要注入的对象注册到 Gone 框架中。

  3. 自动注入和生命周期管理

    在框架启动过程中,自动对所有注册对象进行依赖注入,同时调用如 BeforeInitInitStartStop 等生命周期方法(如果对象实现了对应接口)。

注意: Gone 框架只允许注册实现了 Goner 接口的对象,而通常通过嵌入 gone.Flag 来实现 Goner 接口。这样可以区分哪些对象需要注入、哪些不需要。但这也带来了一个问题:如何将第三方对象注入到框架中?为此,Provider 机制被引入。


2. 不同的 Provider

Gone 中主要支持两种注入方式:按类型注入按名字注入

2.1 按类型注入

当字段未指定特定名称时(标签中使用 * 或省略名称),框架会根据字段的类型查找合适的 Provider。这里定义了两种 Provider 接口:

  • 支持传入参数的 Provider

    go 复制代码
    type Provider[T any] interface {
        Goner
        Provide(tagConf string) (T, error)
    }
  • 无参数的 Provider

    go 复制代码
    type NoneParamProvider[T any] interface {
        Goner
        Provide() (T, error)
    }

示例代码:

go 复制代码
package main

import "github.com/gone-io/gone/v2"

type ThirdBusiness1 struct {
    Name string
}

type ThirdBusiness2 struct {
    Name string
}

// Provider 实现,带参数
type ThirdBusiness1Provider struct {
    gone.Flag
    gone.Logger `gone:"*"`
}

func (p *ThirdBusiness1Provider) Provide(tagConf string) (*ThirdBusiness1, error) {
    p.Infof("tagConf->%s", tagConf)
    return &ThirdBusiness1{Name: "ThirdBusiness1"}, nil
}

// 无参数 Provider 实现
type ThirdBusiness2Provider struct {
    gone.Flag
}

func (p *ThirdBusiness2Provider) Provide() (*ThirdBusiness2, error) {
    return &ThirdBusiness2{Name: "ThirdBusiness2"}, nil
}

type ThirdBusinessUser struct {
    gone.Flag
    thirdBusiness1 *ThirdBusiness1 `gone:"*,AGI"`
    thirdBusiness2 *ThirdBusiness2 `gone:"*"`
}

func main() {
    gone.
        Load(&ThirdBusinessUser{}).
        Load(&ThirdBusiness1Provider{}).
        Load(&ThirdBusiness2Provider{}).
        Run(func(user *ThirdBusinessUser, log gone.Logger) {
            log.Infof("user.thirdBusiness1.name->%s", user.thirdBusiness1.Name)
            log.Infof("user.thirdBusiness2.name->%s", user.thirdBusiness2.Name)
        })
}

运行结果:

log 复制代码
2025/03/11 10:03:22 tagConf->AGI
2025/03/11 10:03:22 user.thirdBusiness1.name->ThirdBusiness1
2025/03/11 10:03:22 user.thirdBusiness2.name->ThirdBusiness2

2.2 按名字注入

当存在相同类型的多个 Provider 时,可以通过名字进行区分。字段上的 gone 标签中指定 Provider 名称,要求 Provider 返回的对象类型与字段类型兼容。

示例代码:

go 复制代码
package main

import "github.com/gone-io/gone/v2"

type ThirdBusiness struct {
    Name string
}

type xProvider struct {
    gone.Flag
}

func (p *xProvider) GonerName() string {
    return "x-business-provider"
}

func (p *xProvider) Provide(tagConf string) (*ThirdBusiness, error) {
    return &ThirdBusiness{Name: "x-" + tagConf}, nil
}

type yProvider struct {
    gone.Flag
}

func (p *yProvider) GonerName() string {
    return "y-business-provider"
}

func (p *yProvider) Provide() (*ThirdBusiness, error) {
    return &ThirdBusiness{Name: "y"}, nil
}

type ThirdBusinessUser struct {
    gone.Flag
    x *ThirdBusiness `gone:"x-business-provider,extend"`
    y *ThirdBusiness `gone:"y-business-provider"`
}

func main() {
    gone.
        Load(&ThirdBusinessUser{}).
        Load(&xProvider{}, gone.OnlyForName()).
        Load(&yProvider{}, gone.OnlyForName()).
        Run(func(user *ThirdBusinessUser, log gone.Logger) {
            log.Infof("user.x.name->%s", user.x.Name)
            log.Infof("user.y.name->%s", user.y.Name)
        })
}

在注册 Provider 时有两种指定名字的方法:

  1. 实现 GonerName() 方法 ------ 返回 Provider 的名称。
  2. Load 时使用 gone.Name("...") 选项 ------ 显式指定 Provider 名称。

提示: 当使用相同类型提供多个 Provider 时,需要使用 gone.OnlyForName() 选项,否则框架会报错;而相同名字的 Provider 只能存在一个,否则也会报错。

2.3 基于名字的多类型 Provider

在某些场景下(如配置注入),希望通过一个 Provider 提供多种类型的对象,此时可以定义 NamedProvider 接口:

go 复制代码
type NamedProvider interface {
    NamedGoner
    Provide(tagConf string, t reflect.Type) (any, error)
}

其中:

  • tagConf 参数为 gone 标签中第一个逗号后面的扩展配置部分。
  • t 参数表示需要注入字段的类型。

这种设计允许一个 Provider 根据字段类型返回对应的实例,实现多类型注入。


3. "星号" Provider:*

gone 标签中的名称为 * 或省略时,表示按类型注入。这实际上是框架中预定义的一个 NamedProvider,其名称就是 *

其工作逻辑为:

  • 根据需要注入字段的类型,自动查找并调用合适的 Provider 来提供值。

总结

Gone V2 通过 Provider 机制实现了灵活的依赖注入,支持:

  • 按类型注入:直接根据字段类型寻找合适的 Provider。
  • 按名字注入:在存在多个相同类型 Provider 时,通过名称进行区分。
  • 多类型 Provider:一个 Provider 可根据字段类型返回不同的对象,适用于配置注入等场景。
  • "星号" Provider:简化按类型注入的处理流程。

这种设计不仅降低了第三方对象与框架耦合度,也大大提高了依赖注入的灵活性和扩展性。


相关推荐
牛奔11 小时前
Go 如何避免频繁抢占?
开发语言·后端·golang
想用offer打牌16 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
passerby606117 小时前
完成前端时间处理的另一块版图
前端·github·web components
KYGALYX17 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了17 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
爬山算法18 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
Moment18 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
草梅友仁19 小时前
墨梅博客 1.4.0 发布与开源动态 | 2026 年第 6 周草梅周报
开源·github·ai编程
Cobyte19 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc
程序员侠客行20 小时前
Mybatis连接池实现及池化模式
java·后端·架构·mybatis