- 在插件系统中添加 PluginHelper 结构体,简化插件方法的自动发现和注册。 - 更新 BasePluginImpl 以支持通过辅助器执行插件方法,增强 Execute 方法的灵活性。 - 统计插件和示例程序中实现新的方法调用方式,展示如何使用自动注册的方法。 - 通过结构体参数传递,简化插件调用过程,提升用户体验。 此更新提升了插件系统的可扩展性和易用性,便于开发者动态管理和执行插件功能。
125 lines
4.3 KiB
Markdown
125 lines
4.3 KiB
Markdown
# 插件自动方法注册与调用机制
|
||
|
||
## 简介
|
||
|
||
插件自动方法注册与调用机制是一个基于 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. **考虑向后兼容性**:新增方法不会影响现有功能,是扩展插件功能的理想方式 |