插件化开发提供了很多便利,可动态扩展程序的相关功能,如Windows中的DLL、Linux中的So文件、还有IDEA中的插件,应用范围不可谓不广;
在Golang中提供了自己的插件机制,可使用其进行插件化开发;在Golang的plugin包中提供了加载插件、调用插件中函数的相关方法;
Golang中的插件机制使用非常简单;就只有这么三步:
1、编写插件库相关代码,生成so文件;
2、加载插件so文件;
3、调用相关的函数或字段;
下面用一个简单例子来看看插件的基本使用:
一、定义插件
这个插件非常简单,定义一个公开的字段V,一个公开的函数F。
package main
import “fmt”
var V int
func F(){
fmt.Printf(“Hello Plugin: %d\n”,V)
}
二、生成插件so文件
生成so文件也和之前编译普通的go文件一样只是多了个参数;
go build -buildmode=plugin demo.go
执行上诉命令后将生成一个名为:demo.so的插件库文件;
三、使用插件
有了插件现在到了使用插件,使用插件也没啥难度,具体代码如下:
p,err := plugin.Open(“demo.so”)
if err!=nil{
panic(err)
}
v,err := p.Lookup(“V”) //调用插件中的V字段
if err!=nil{
panic(err)
}
f,err :=p.Lookup(“F”) //调用插件中的F函数
if err!=nil{
panic(err)
}
*v.(*int) = 100
f.(fun())() //类型断言,f为一个无参无返回值的函数fun(),然后调用函数f
插件调用的输出结果为:Hello Plugin: 100
插件使用:
插件与主程序使用的接口必须是同一个
通常使用流程:
1、定义依赖的包文件,包含相关的接口等;
2、插件实现上述所定义的接口
3、主程序应用依赖包,调用插件实现;
一、包接口
package lib
type Calculator interface {
Add(int, int) int
Sub(int, int) int
}
二、插件实现
package main
import (
"demo/lib"
)
type Call struct {}
func (c *Call) Sub(a, b int) int {
return a - b
}
func (c *Call) Add(a, b int) int {
return a + b
}
func NewCal() lib.Calculator {
return &Call{}
}
三、主程序引用包,调用插件的相关实现
package main
import (
"demo/lib"
"fmt"
"plugin"
)
p, err := plugin.Open("plugin/operation_plugin.so")
if err != nil {
panic(err)
}
cal,err:= p.Lookup("NewCal")
if err != nil {
panic(err)
}
c:= cal.(func ()lib.Calculator)()
fmt.Println(c.Add(3,5))
fmt.Println(c.Sub(20,10))
注意事项:
编译插件所使用的参数必须与编译主程序所使用参数一直,否则将可能出现如下异常信息:
panic: plugin.Open("plugin/operation_plugin"): plugin was built with a different version of package runtime/internal/sys
如在编译插件库so文件时使用了-gcflags all=-N -l 参数,禁止优化、禁止内联,则主程序调用插件时也必须加上;