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

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

125 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 插件自动方法注册与调用机制
## 简介
插件自动方法注册与调用机制是一个基于 Go 反射的工具,它能够自动发现插件中的方法,并使这些方法可以通过统一的 `Execute` 接口调用,而无需手动为每个方法编写对应的处理代码。
这个机制大大简化了插件开发,提高了代码的可维护性,并降低了出错的可能性。
## 工作原理
1. 在插件初始化时,通过反射获取插件实例的所有可用方法
2. 将这些方法注册为可通过 `Execute` 调用的动作,动作名称为方法名的小写形式
3. 当调用 `Execute` 方法时,自动查找对应的方法并调用,处理参数转换和错误处理
## 使用方法
### 1. 创建插件
创建插件时,只需继承 `BasePluginImpl` 结构体,然后实现你的方法:
```go
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. **普通参数**:可以使用基本类型参数,如 `string``int``bool`
3. **结构体参数**:可以定义结构体参数,用于接收多个参数,结构体字段名会被用作参数名
4. **返回值**
- 单个返回值:直接作为操作结果返回
- 两个返回值,第二个为 `error`:遵循 Go 惯例,返回结果和错误
- 多个返回值:打包成 map 返回
### 3. 结构体参数示例
使用结构体参数可以使方法定义更加清晰,并支持更复杂的参数:
```go
// 参数结构体
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
}
```
调用示例:
```go
// 调用带结构体参数的方法
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` 方法,返回所有可用的操作列表:
```go
func (p *MyPlugin) GetAvailableOperations() []string {
return p.GetAvailableActions()
}
```
调用示例:
```go
// 获取可用操作
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. **考虑向后兼容性**:新增方法不会影响现有功能,是扩展插件功能的理想方式