有时候设计不能一簇而就,在主体完成后,完善模块功能时,就会用到插件技术。
通过网友的文章学习学习。
注意: 插件技术只能在Linux下, windows 下 Golang不支持动态库。
package main
import (
"fmt"
"time"
)
// main 主体程序入口
func main() {
nowSecond := time.Now().Second()
doPrint(nowSecond)
fmt.Println("Process Stop ========")
}
// 执行打印操作
func doPrint(nowSecond int) {
if nowSecond%2 == 0 {
printWorld() //偶数
} else {
printHello() //奇数
}
}
// 执行打印hello
func printHello() {
fmt.Println("hello")
}
// 执行打印world
func printWorld() {
fmt.Println("world")
}
以上代码,实现不断显示字符串
以下为插件代码,注意并没有main函数。只是用来输出一个时间。
package main
import (
"fmt"
"time"
)
// 打印当前时间
func PrintNowTime(){
fmt.Println(time.Now().Second())
}
编译成so文件:go build –buildmode=plugin -o HelloPlugin.so HelloPlugin.go
修改一下主程序,引用插件。
package main
import (
"fmt"
"plugin"
"time"
)
func main() {
nowSecond := time.Now().Second()
doPrint(nowSecond)
fmt.Println("Process Stop ========")
}
// 执行打印操作
func doPrint(nowSecond int) {
if nowSecond%2 == 0 {
printWorld() //偶数
} else {
printHello() //奇数
}
}
// 执行打印hello
func printHello() {
// 执行插件调用
if pluginFunc != nil{
//将存储的信息转换为函数
if targetFunc, ok := pluginFunc.(func()); ok {
targetFunc()
}
}
fmt.Println("hello")
}
// 执行打印world
func printWorld() {
fmt.Println("world")
}
// 定义插件信息
const pluginFile = "HelloPlugin.so"
// 存储插件中将要被调用的方法或变量
var pluginFunc plugin.Symbol
// init 函数将于 main 函数之前运行
func init() {
// 查找插件文件
pluginFile, err := plugin.Open(pluginFile)
if err != nil {
fmt.Println("An error occurred while opening the plug-in")
} else{
// 查找目标函数
targetFunc, err := pluginFile.Lookup("PrintNowTime")
if err != nil {
fmt.Println("An error occurred while search target func")
}
pluginFunc = targetFunc
}
fmt.Println("Process On ==========")
}
通过初始化时,将指定文件的函数引用。但这样的问题在于必须指定文件名,而不是无感化。
先通过遍历目录下的文件,但该如何装载呢。
创建一个单独处理插件的文件pluginSupport.go
package PluginTest
import (
"fmt"
"path"
"plugin"
)
// PluginItem 存储着插件的信息
type PluginItem struct {
Name string
TargetFunc plugin.Symbol
}
// 所有插件必须实现该方法
const TargetFuncName = "TargetFunc"
// LoadAllPlugin 将会过滤一次传入的targetFile,同时将so后缀的文件装载,并返回一个插件信息集合
func LoadAllPlugin(targetFile []string) []PluginItem {
var res []PluginItem
for _, fileItem := range targetFile {
// 过滤插件文件
if path.Ext(fileItem) == ".so" {
pluginFile, err := plugin.Open(fileItem)
if err != nil {
fmt.Println("An error occurred while load plugin : [" + fileItem + "]")
fmt.Println(err)
}
//查找指定函数或符号
targetFunc, err := pluginFile.Lookup(TargetFuncName)
if err != nil {
fmt.Println("An error occurred while search target func : [" + fileItem + "]")
fmt.Println(err)
}
//采集插件信息
pluginInfo := PluginItem{
Name: fileItem,
TargetFunc: targetFunc,
}
// 进行调用
if f, ok := targetFunc.(func()); ok {
f()
}
res = append(res, pluginInfo)
}
}
return res
}
这样就实现在了插件的调入,但仅仅是引入的时候运行了一个函数而已。