Files
goproxy/examples/plugin/docs/autoreflect.md
DarkiT b6bf2c5699 增强插件系统:引入插件辅助器和自动方法发现功能
- 在插件系统中添加 PluginHelper 结构体,简化插件方法的自动发现和注册。
- 更新 BasePluginImpl 以支持通过辅助器执行插件方法,增强 Execute 方法的灵活性。
- 统计插件和示例程序中实现新的方法调用方式,展示如何使用自动注册的方法。
- 通过结构体参数传递,简化插件调用过程,提升用户体验。

此更新提升了插件系统的可扩展性和易用性,便于开发者动态管理和执行插件功能。
2025-03-14 11:37:42 +08:00

4.3 KiB
Raw Blame History

插件自动方法注册与调用机制

简介

插件自动方法注册与调用机制是一个基于 Go 反射的工具,它能够自动发现插件中的方法,并使这些方法可以通过统一的 Execute 接口调用,而无需手动为每个方法编写对应的处理代码。

这个机制大大简化了插件开发,提高了代码的可维护性,并降低了出错的可能性。

工作原理

  1. 在插件初始化时,通过反射获取插件实例的所有可用方法
  2. 将这些方法注册为可通过 Execute 调用的动作,动作名称为方法名的小写形式
  3. 当调用 Execute 方法时,自动查找对应的方法并调用,处理参数转换和错误处理

使用方法

1. 创建插件

创建插件时,只需继承 BasePluginImpl 结构体,然后实现你的方法:

type MyPlugin struct {
    *plugin.BasePluginImpl
    // 插件的属性
}

// 创建导出的插件变量
var Plugin = &MyPlugin{
    BasePluginImpl: plugin.NewPlugin(
        "MyPlugin",
        "1.0.0",
        "我的插件",
        "开发者",
        plugin.PluginTypeUtils,
    ),
    // 初始化属性
}

// 这个方法会被自动注册为 "doSomething" 操作
func (p *MyPlugin) DoSomething(name string, value int) (string, error) {
    // 方法实现
    return fmt.Sprintf("%s: %d", name, value), nil
}

2. 方法参数约定

方法的参数和返回值需要遵循一定的约定,以便自动注册系统能够正确处理:

  1. Context 参数:如果方法的第一个参数是 context.Context,调用时会自动传入上下文参数
  2. 普通参数:可以使用基本类型参数,如 stringintbool
  3. 结构体参数:可以定义结构体参数,用于接收多个参数,结构体字段名会被用作参数名
  4. 返回值
    • 单个返回值:直接作为操作结果返回
    • 两个返回值,第二个为 error:遵循 Go 惯例,返回结果和错误
    • 多个返回值:打包成 map 返回

3. 结构体参数示例

使用结构体参数可以使方法定义更加清晰,并支持更复杂的参数:

// 参数结构体
type RequestParams struct {
    Name          string  `json:"name"`
    Count         int     `json:"count"`
    IsEnabled     bool    `json:"isEnabled"`
    Metadata      map[string]string `json:"metadata"`
}

// 使用结构体参数的方法
func (p *MyPlugin) ProcessRequest(ctx context.Context, params RequestParams) (interface{}, error) {
    // 处理请求
    return result, nil
}

调用示例:

// 调用带结构体参数的方法
result, err := pm.ExecutePlugin(ctx, "MyPlugin", "processRequest", map[string]interface{}{
    "name": "测试请求",
    "count": 5,
    "isEnabled": true,
    "metadata": map[string]string{
        "key1": "value1",
        "key2": "value2",
    },
})

4. 获取可用操作列表

插件可以实现 GetAvailableOperations 方法,返回所有可用的操作列表:

func (p *MyPlugin) GetAvailableOperations() []string {
    return p.GetAvailableActions()
}

调用示例:

// 获取可用操作
operations, err := pm.ExecutePlugin(ctx, "MyPlugin", "getAvailableOperations", nil)
fmt.Println("可用操作列表:", operations)

注意事项

  1. 方法命名:方法名会被转换为小写形式作为操作名,调用时不区分大小写
  2. 跳过接口方法:接口方法如 Name()Version() 等不会被注册为可调用操作
  3. 参数转换:系统会尝试进行参数类型转换,但复杂的自定义类型可能需要手动处理
  4. 错误处理:如果找不到对应的方法,会回退到基础的 Execute 实现
  5. 性能考虑:反射调用的性能略低于直接调用,但在大多数场景下差异可忽略不计

最佳实践

  1. 使用结构体参数:对于多参数方法,使用结构体参数会使代码更清晰
  2. 提供帮助方法:实现 GetAvailableOperations 方法,便于用户了解插件支持的操作
  3. 遵循命名约定:方法名使用驼峰命名法,参数名使用小写
  4. 添加适当的文档:为方法添加注释,说明参数和返回值的含义
  5. 考虑向后兼容性:新增方法不会影响现有功能,是扩展插件功能的理想方式