Go 语言编译模式 (buildmode) 之插件模式(plugin)

yufei       3 年, 7 月 前       3803

Go 语言的编译模式,简单的来说就是在使用 go buildgo install 两个命令编译代码的时候要生成什么养的文件。

使用方式简单来说就是

go build -buildmode=<mode>

go install -buildmode=<mode>

我们今天要讲是的 插件模式 plugin

插件模式维基百科 上是这么定义的 插件(又译外挂,英文为plug-in、plugin、add-in、addin、add-on、addon或extension)是一种电脑程序,透过和应用程序(例如网页浏览器,电子邮件客户端)的互动,用来替应用程序增加一些所需要的特定的功能。最常见的有游戏、网页浏览器的插件和媒体播放器的插件

但是,维基百科上对 插件 的描述只是解释了什么是插件,但并没有说插件是如何工作的。如果我们翻看 百度百科,就很明了了 : 插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。其只能运行在程序规定的系统平台下(可能同时支持多个平台),而不能脱离指定的平台单独运行。因为插件需要调用原纯净系统提供的函数库或者数据

插件静态库动态库 不同,后两者没有规范一说,反正只要是 导出 的都能用,而且必须在代码编译时就指定。但是 插件 却需要遵循一定的 应用程序接口,从 Go 语言的角度来说,就是要符合一定的 接口 interface{} ,而且,插件一般是动态调用的,不需要在编译时就指定。

plugin 插件模式的几个主要点:

经过上面的一番说明,想必大家对 插件 已经有了一个模糊的概念,可能还不能具体细化,下面,我们就来看看插件模式的几个主要要点:

  1. 如果一个应用程序可以使用插件模式,那么它必须为符合它的插件定制一个 接口 interface{},指明它是通过哪些函数来运作的。
  2. 编译的时候 -buildmode=plugin
  3. 编译结果只有一个文件,一个 LSB shared object 类型的文件。
  4. 插件里的代码必须完全实现接口,这是 Go 语言接口特性所决定的。
  5. 应用程序的插件必须放在指定的目录,这样才能动态加载。
  6. 应用程序调用插件的时候必须使用官方的插件库

上面这些要点,按照官方的要求的话,只有 1 2 6 是必须的,其它都是我们实践出来的。

范例

  1. 首先使用下面的命令行创建项目

    mkdir bm
    cd bm
    go mod init bm
    touch main.go greet.go
    

    main.go 用于编写应用程序,而 greet.go 用来编写插件。

  2. 其次,我们需要为我们的插件定义一个接口 interface{},比如我们觉得我们的插件都要实现 Greet() 方法。

    type Greeter interface {
        Greet()
    }
    
  3. 使用你最喜欢的编辑器打开 greet.go 文件然后复制下面的代码

    package main
    
    type greeting string
    
    func (g greeting) Greet() {
        println("hello world")
    }
    
    var Greeter greeting
    
  4. 然后我们使用下面的命令来编译

    go build -o greet.so -buildmode=plugin plugin.go
    

    编译有点耗时,具体多久取决于你加载了多少类库,编译完成后我们可以在当前名录下发现一个新文件 greet.so

    yufei@twle.cn bm % ls
    greet.so    main.go     plugin.go  go.mod
    

    我们可以使用 file 命令查看下该文件

    额,linuxmac 操作系统下才有

    yufei@twle.cn bm % file greet.so 
    greet.so: Mach-O 64-bit dynamically linked shared library x86_64
    
  5. 接着我们就可以写一个应用程序来点用我们的插件了。

    这个应用程序我们必须导入 plugin 这个标准库。然后声明我们的插件实现了哪个接口。

    package main
    
    import (
        "os"
        "fmt"
        "plugin" // 1. 导入标准库
    )
    
    // 2. 为插件定义接口
    type Greeter interface {
        Greet()
    }
    
    func main() {
    
        // 3. 查找并实例化插件
        plug, err := plugin.Open("./greet.so")
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
    
        // 4. 找到插件导出的接口实例,其实这个不是必须的
        symGreeter, err := plug.Lookup("Greeter")
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
    
        // 5. 类型转换
        var greeter Greeter
        greeter, ok := symGreeter.(Greeter)
        if !ok {
            fmt.Println(err)
            os.Exit(1)
        }
    
        // 6. 调用方法
        greeter.Greet()
    }
    

    总的来说分为 6 个步骤。恩,差不多缺一不可吧。

  6. 然后我们就可以使用 go run main.go 查看结果了

    yufei@twle.cn bm % go run main.go
    hello world
    
目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.