软件的插件原理

5 篇文章 0 订阅
5 篇文章 0 订阅

常见软件的插件原理?

很多软件都支持安装插件,比如我们常用的ide都是支持安装插件的。

手动安装插件

在windows/linux下手动安装插件。

  1. 下载插件
  2. 插件往往是一个dll、so文件或者zip等压缩文件
  3. 将下载的插件放入软件的插件目录
  4. 重启软件

背后原理

  1. 下载的插件是一个dll、so库。
  2. 将这个库放入插件目录是为了让软件自动加载这个插件。
  3. 软件怎么加载呢?首先扫描插件目录,将所有dll、so加载,然后检查dll、so是否存在软件的接口函数。
  4. 软件需要定义插件的接口函数。然后在加载dll、so后提取相关接口函数,最后实现插件的安装。

动手写一个可安装插件的程序

  1. 插件接口
    • Print(interface{})
    • Install() Printor
  2. 插件目录 ./plugins
  3. linux环境

预备知识

  1. plugin模式
  2. 插件运行方式
  3. go plugin包使用

相关知识

go build 可以指定buildmode。分为了多种模式。具体模式如下。

模式说明
当前go版本1.10.3
archive编译成二进制文件。一般是静态库文件。 xx.a
c-archive编译成C归档文件。C可调用的静态库。xx.a。注意要编译成此类文件需要import C 并且要外部调用的函数要使用 “//export 函数名” 的方式在函数上方注释。否则函数默认不会被导出。
c-shared编译成C共享库。同样需要 import “C” 和在函数上方注释 // export xxx
default对于有main包的直接编译成可执行文件。没有main包的,编译成.a文件
exe编译成window可执行程序
plugin将main包和依赖的包一起编译成go plugin。非main包忽略。【类似C的共享库或静态库。插件式开发使用】

实例

文件结构:
    -softplugin         //根目录
        -soft           //软件目录
        -plugins         //插件目录
        -itf            //接口目录

无自定义数据

// plugins/hello.go
package main
import "fmt"
func Hello(){
    fmt.Println("hello")
}
// go build -buildmode=plugin -o hello.so hello.go


// soft/basetype.go
package main
import (
    "os"
    "path"
    "plugin"
    "fmt"
)

func main(){
    //加载插件
    pluginDir := "../plugins"
    //扫描文件夹下所有so文件
    f, err := os.OpenFile(pluginDir, os.O_RDONLY, 0666)
    if err != nil {
        panic(err)
    }
    fi, err := f.Readdir(-1)
    if err != nil {
        panic(err)
    }
    plugins := make([]os.FileInfo, 0)
    for _, ff := range fi {
        if ff.IsDir() || path.Ext(ff.Name()) != ".so" {
            continue
        }
        plugins = append(plugins, ff)
        pdll, err := plugin.Open(pluginDir + "/" + ff.Name())
        if err != nil {
            fmt.Println(err)
            continue
        }
        plg, err := pdll.Lookup("Hello")
        if err != nil {
            panic(err)
        }
       plg.(func())()
    }
}
// go run basetype.go

定义插件接口 interface。

//------------------------------------------------------
// itf/itf1.go
package itf
type Printor interface{
    Print(v interface{})
}

//------------------------------------------------------
// plugins/p1.go
package main
import (
    "fmt"
    "softplugin/itf"
)
type ScreenPrintor struct{}
func (ScreenPrintor)Print(v interface{}){
    fmt.Println("p1p1 ",v)
}
func Install() Printor{
    return &ScreenPrintor{}
}

//-----------------------------------------------------
// soft/s1.go
package main
import (
    "softplugin/itf"
    "os"
    "path"
    "plugin"
    "fmt"
)
var(
    printors = make([]itf.Printor, 0)
)

func main(){
    //加载插件
    pluginDir := "../plugins"
    //扫描文件夹下所有so文件
    f, err := os.OpenFile(pluginDir, os.O_RDONLY, 0666)
    if err != nil {
        panic(err)
    }
    fi, err := f.Readdir(-1)
    if err != nil {
        panic(err)
    }
    plugins := make([]os.FileInfo, 0)
    for _, ff := range fi {
        if ff.IsDir() || path.Ext(ff.Name()) != ".so" {
            continue
        }
        plugins = append(plugins, ff)
        pdll, err := plugin.Open(pluginDir + "/" + ff.Name())
        if err != nil {
            fmt.Println(err)
            continue
        }
        plg, err := pdll.Lookup("Hello")
        if err != nil {
            panic(err)
        }
       printors = append(printors, (plg.(func() itf.Printor))())
    }
    for _, p := range printors {
        p.Print("pppp")
    }
    
}

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值