golang 快速为项目添加插件功能

首先,了解下go自带的plugin包,其中open方法会返回plugin.Plugin的指针,和error

pluginer, err := plugin.Open("your plugin file path") //return plugin.Plugin,error

然后就是plugin.Plugin的指针,open方法只是个引路人,最后拿到插件中的方法和变量还是要靠你取出来的*plugin.Plugin.
接下来的步骤就更简单粗暴了,通过你的方法名或变量名取出来再在你的项目中进行"实例化"。

sym,err:=pluginer.Lookup("your variable(function) name")
//假设取出的变量是string
res:=*sym.(*string)
//如果是方法的话,就是以下这样。省略号处是你该放的参数和返回
res:=sym.(func(...)...)

这lookup出来的也不是什么高深的东西(symbol)

go语言通过浏览器插件控制浏览器_主函数


go语言通过浏览器插件控制浏览器_golang_02


,说直白点就是它lookup只是看看有没有这个命名的东西(他也不知道找到是变量还是方法,他只管有没有)。找出来确认有这东西过后再给他个类型。所以这里如果你的项目要设计插件这个模块的话,你得提前留接口,你的插件再跟着你设计的接口两个对接开发。当然作为个人开发者,每次重复写插件读取,和插件加载的方法是很烦的(如果个人对这种重复造轮子的行为和操作感觉不强烈的话,那肯定无所谓的哈哈哈哈).

快速打造插件功能(基于octools)

命令行拉取octools

#v1.1 和last都可以,last经常改动,v1.1改动较少,带beta的版本不能用(bug超多)
go get github.com/oswaldoooo/octools@v1.1
  • 读取配置文件
<?xml version="1.0" encoding="UTF-8"?>
<plugin_info>
 				
    <plugin classname="plugin's classname" name="plugin's filename"/>
    <rootpath>your project's rootpath</rootpath>
</plugin_info>

这里的plugin 这个项可以有多个(毕竟都是插件化了,你插件肯定不止一个啊,不然直接写到项目中多好)
classname是用于判断你项目属于哪一个接口的,每一类的接口对应一个classname(也许你实现相同的功能由于不同场景需要通过不同的算法来完成呢)
name就是你插件的名字(带不带.so的后缀都无所谓),rootpath是你项目的根目录,插件应该放在根目录下plugin目录里面

导入包

import "github.com/oswaldoooo/octools/pluginer"

在你项目中的使用

pluginer, err := pluginer.CreatePluginer("上文配置文件的存放位置", coremap)
//读取上文配置文件,得到插件的讯息,coremap就是接口名:对接口的解析方法。map[string]func(*plugin.Plugin)error

也许你会想我解析出来的方法放哪里,因为此处设计的初衷是从插件取出来的变量和方法默认全局。

案列展示

主函数

package main

import (
	"flag"
	"fmt"
	"plugin"

	"github.com/oswaldoooo/octools/pluginer"
)
var coremap = map[string]func(*plugin.Plugin) error{"test": loadnormal}
func init() {
	pluginer, err := pluginer.CreatePluginer("/Users/oswaldo/dev/golang/examples/site.xml", coremap)
	if err == nil {
	//简单寻找,满足func(*plugin.Plugin)error解析方法接口的插件都会被初始化,而这些解析方法来源于你的coremap
		errs := pluginer.LookUpAll()
		if len(errs) > 0 {
			fmt.Println(errs)
		}
	}
}
var testfunc = make(map[string]func(string, *int) error)
//一个普通的接口解析方法展示
func loadnormal(pluginer *plugin.Plugin) (err error) {
	//变量搜寻展示
	srm, err := pluginer.Lookup("Pattern")
	if err == nil {
		pattern := *srm.(*string)
		//方法搜寻展示
		srm, err = pluginer.Lookup("Greet")
		if err == nil {
			resfunc := srm.(func(string, *int) error)
			//将找到的方法放入全局级容器,以供主函数日后调用,这里后面可多加一个boolean类型的返回值来确定它是不是func
			testfunc[pattern] = resfunc
		}
	}
	return
}
var method = flag.String("method", "", "input your method")
var val = flag.String("value", "", "input some words")

func main() {
//这里模仿你的项目日常使用中调用指定的插件进行处理
	flag.Parse()
	length := new(int)
	if usefunc, ok := testfunc[*method]; ok {
		err := usefunc(*val, length)
		if err == nil {
			fmt.Println("words length", *length)
		} else {
			fmt.Println("error>>", err.Error())
		}
	} else {
		fmt.Println("not", *method, "is not existed")
	}
}

插件中的go方法张这样

//切记,写插件一定的main,别整个其他包名,最后编译不出来
package main

import "fmt"
//这就是上文搜寻的Pattern,这里它本尊张这样
var Pattern = "test"
//上文搜寻的Greet方法
func Greet(input string, wordslen *int) (err error) {
	fmt.Println("hello,world,im greet plugin.your input is", input)
	*wordslen = len(input)
	return
}

结果展示

从上面主函数你可以看见我是没写测量长度方法的,这个测量长度方法是我对应着插件接口写出来,然后编译后放入其根目录下中plugin的目录中,最后项目初始化再将其载入程序中的

go语言通过浏览器插件控制浏览器_主函数_03