最近研究了一下jna,发现可以用go干点事情。
基本步骤:
- 安装go (这句就是废话)
- 安装docker(用来下载用户交叉编译的镜像文件,该文件包含各个平台的gcc)
- 安装xgo工具(第三方工具,比自己用CC编译方便太多)
- 编写计划被编译成动态库文件的go源码
- 编写java代码,使用jna连接动态库函数
一、安装go
略过,本人安装的是 go 1.11
二、安装docker
仍然略过
三、安装xgo工具
第三方工具,比自己用CC指定gcc方便太多
- 首先使用docker下载含有依赖的各平台gcc工具包镜像
docker pull karalabe/xgo-latest
这个镜像由于属于 all-in-one的形式,所以会很大,请耐心等待。。。。
- 安装xgo工具:安装前,如果开启了module的话,建议关闭掉
go get -u github.com/karalabe/xgo
- 测试安装是否成功
@admin.local ➜ gopath xgo
Checking docker installation...
Client: Docker Engine - Community
Version: 18.09.2
API version: 1.39
Go version: go1.10.8
Git commit: 6247962
Built: Sun Feb 10 04:12:39 2019
OS/Arch: darwin/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 18.09.2
API version: 1.39 (minimum version 1.12)
Go version: go1.10.6
Git commit: 6247962
Built: Sun Feb 10 04:13:06 2019
OS/Arch: linux/amd64
Experimental: true
2019/05/29 18:09:35 Usage: xgo [options] <go import path>
至此,xgo安装大功告成;具体使用,后面单说
四、编写要编译为动态库函数的go代码
package main
import "C"
import "fmt"
//export Sum
func Sum(a int, b int) int {
return a + b
}
//export Name
func Name(fname *C.char) *C.char {
result := fmt.Sprintf("Hello, %s", C.GoString(fname))
fmt.Printf(result)
return C.CString(result)
}
func main() {
//fmt.Printf(string(Name("abc")))
}
- 其中,如果要将go使用cgo编译为动态库函数,需要源码包中导入C
import "C"
- 再次,要导出的函数前必须要进行 export 该函数
//export Sum
- 要导出的动态函数,必须在main包下,且必须包含main函数
func main(){}
- 关于go语音中的string类型,使用c导出时,使用 *C.char ,方便Java传递和接收String类型参数;go中将 C.char 参数转为string使用 C.GoString() 函数;go中将string类型转为C.char使用 C.CString() 函数
五、编译动态库文件
由于库文件运行的平台不一样,所需文件也不一样,所以我们要确定运行的平台。我这边需要再mac和ubuntu下运行。查看ubuntu系统指令集:
root@a:/usr/local/tomcat/bin# dpkg --print-architecture
amd64
说明我们目标系统
GOOS=linux
GOARCH=amd64
确定好后,回过头来,使用我们的交叉编译工具xgo,进行交叉编译,生成目标平台动态库文件:
bash-3.2$ xgo -buildmode=c-shared -out libwxsum --targets=linux/amd64,darwin/amd64 .
Checking docker installation...
Client: Docker Engine - Community
Version: 18.09.2
API version: 1.39
Go version: go1.10.8
Git commit: 6247962
Built: Sun Feb 10 04:12:39 2019
OS/Arch: darwin/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 18.09.2
API version: 1.39 (minimum version 1.12)
Go version: go1.10.6
Git commit: 6247962
Built: Sun Feb 10 04:13:06 2019
OS/Arch: linux/amd64
Experimental: true
Checking for required docker image karalabe/xgo-latest... found.
Cross compiling wx_mini...
Building locally wx_mini...
Compiling for linux/amd64...
go get: warning: modules disabled by GO111MODULE=auto in GOPATH/src;
ignoring go.mod;
see 'go help modules'
Cleaning up build environment...
其中命令为:
xgo -buildmode=c-shared -out libwxsum --targets=linux/amd64,darwin/amd64 .
-buildmode=c-shared : 说明要编译为动态库函数方便jna调用
-out libwxsum : 说明我们要生成的各个平台下动态库函数的前缀
--targets=linux/amd64,darwin/amd64 : 说明我们生成目标为
GOOS=linux GOARCH=amd64 //ubuntu
GOOS=darwin GOARCH=amd64 //MacOS
的动态库函数。
编译完成后可以看到生成的指定平台下的动态库函数:
bash-3.2$ ls
libwxsum-darwin-10.6-amd64.dylib libwxsum-linux-amd64.so
go.mod libwxsum-darwin-10.6-amd64.h main.go
go.sum libwxsum-linux-amd64.h util
其中的 libwxsum-linux-amd64.so 为生成的对应 ubuntu 下的动态库函数,libwxsum-darwin-10.6-amd64.dylib 为 MacOS下 的动态库函数。
至此,动态库函数的交叉编译完成。
注:如果还需要编译其他包下文件(如上的util包依赖了其他的开源框架),需要使用
--pkg util/goimports 开源框架git地址
六、编写Java代码使用动态库函数
- java工程使用maven管理,最终部署为web应用,需要添加jna依赖
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.3.1</version>
</dependency>
- 创建接口继承jna的Library,并定义出要调用的动态库函数
public interface LibSum extends Library {
LibSum INSTANCE = (LibSum) Native.loadLibrary("wxsum", LibSum.class);
int Sum(int a, int b);
String Name(String fname);
}
其中,loadLibrary函数中的第一个参数,标明动态库函数名,该名称为我们之前生成动态库函数名字,去掉前缀 lib 的名字,并且为了方便跨平台,后面的扩展名也会由 jna 根据当前运行环境自动补齐调用。
- 编写web请求调用
@RequestMapping("sum")
@ResponseBody
public ResultModel sum(
@RequestParam(name = "a", required = true) int a,
@RequestParam(name = "b", required = true) int b,
@RequestParam(name = "w", required = true) String w) {
int result = LibSum.INSTANCE.Sum(a, b);
String name = LibSum.INSTANCE.Name(w);
return ResultModel.getSuccess(name + " ; Your score is : " + result);
}
- 将动态库函数放入指定目录,本人编写的是web应用且需要再ubuntu下运行,所以需要将库函数放在 resources/linux-x86-64 下(linux-x86-64是jna根据当前系统自动增加的)。
至此,我们编写的使用go函数生成各平台动态库的列子就算完成了。
注:
- 我们可以使用
System.setProperty("jna.debug_load","true");
打印动态库函数调用路径,便于观察库文件加载过程
- 可以使用
System.setProperty("jna.library.path", "/usr/local/tomcat/webapps/ROOT/solib/");
指定加载库文件路径
网友评论