(摘) Golang插件技术

声明:内容源自网络,版权归原作者所有。若有侵权请联系我们

有时候设计不能一簇而就,在主体完成后,完善模块功能时,就会用到插件技术。

通过网友的文章学习学习。

注意: 插件技术只能在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
}

这样就实现在了插件的调入,但仅仅是引入的时候运行了一个函数而已。

相关文章