Go 语言的编译模式,简单的来说就是在使用 go build
或 go 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 插件模式的几个主要点:
经过上面的一番说明,想必大家对 插件 已经有了一个模糊的概念,可能还不能具体细化,下面,我们就来看看插件模式的几个主要要点:
- 如果一个应用程序可以使用插件模式,那么它必须为符合它的插件定制一个 接口
interface{}
,指明它是通过哪些函数来运作的。 - 编译的时候
-buildmode=plugin
- 编译结果只有一个文件,一个
LSB shared object
类型的文件。 - 插件里的代码必须完全实现接口,这是
Go
语言接口特性所决定的。 - 应用程序的插件必须放在指定的目录,这样才能动态加载。
- 应用程序调用插件的时候必须使用官方的插件库
上面这些要点,按照官方的要求的话,只有 1 2 6 是必须的,其它都是我们实践出来的。
范例
-
首先使用下面的命令行创建项目
mkdir bm cd bm go mod init bm touch main.go greet.go
main.go
用于编写应用程序,而greet.go
用来编写插件。 -
其次,我们需要为我们的插件定义一个接口
interface{}
,比如我们觉得我们的插件都要实现Greet()
方法。type Greeter interface { Greet() }
-
使用你最喜欢的编辑器打开
greet.go
文件然后复制下面的代码package main type greeting string func (g greeting) Greet() { println("hello world") } var Greeter greeting
-
然后我们使用下面的命令来编译
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
命令查看下该文件额,
linux
和mac
操作系统下才有yufei@twle.cn bm % file greet.so greet.so: Mach-O 64-bit dynamically linked shared library x86_64
-
接着我们就可以写一个应用程序来点用我们的插件了。
这个应用程序我们必须导入
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 个步骤。恩,差不多缺一不可吧。
-
然后我们就可以使用
go run main.go
查看结果了yufei@twle.cn bm % go run main.go hello world