Java使用JNA调用Golang编译生成的动态库(dll 和 so 文件)

参考文档:

 

1. 基本原理

Java Native Access(JNA)提供一组Java工具类用于在运行期间动态访问系统本地库(Native library:如 windows的 dll文件、linux的so文件),而不需要编写任何Native/ JNI 代码。开发人员只要在一个java接口中描述目标 native library 的函数与结构,JNA 将自动实现 Java 接口到 native function的 映射。

java type 和 native type 在进行转换的时候,是有一个对应的映射关系的,根据JNA的官方文档(详细请见 Type Mapping的官方描述),以下在表格中列出了部分 type-mapping的关系,这些关系也将会在后面的case中进行测试:

C TypeNative RepresentationJava Type
char8-bit integerbyte
int32-bit integerint
intboolean flagboolean
float32-bit floating pointfloat
double64-bit floating pointdouble
pointer (e.g. void*),
array
32- or 64-bit pointer to memory (argument/return)
contiguous memory (struct member)
<P>[] (array of primitive type)
In addition to the above types, which are supported at the native layer, the JNA Java library automatically handles the following types. All but NativeMapped and NativeLong are converted to Pointer before being passed to the native layer.
const char*NUL-terminated array (native encoding or jna.encoding)String
struct*
struct
pointer to struct (argument or return) (or explicitly)
struct by value (member of struct) (or explicitly)
Structure

然后基于go语言中的cgo工具,可以将 C type 转化为 Go Type

 

2. 编写Go函数

以下编写6个case进行测试,前3个case是基本类型(int、double、boolean)在java与go之间的传递,后3个case都是string类型的传递。

package main

import "C"
import (
	"fmt"
	"math"
	"unsafe"
)

// java:int --> C:int == C.int --> Go:int
//export Add
func Add(a, b C.int) C.int {
	aGo := int(a)
	bGo := int(b)
	res := aGo + bGo
	return C.int(res)
}

// java:double --> C:double == C.double --> Go:float64
//export Cosine
func Cosine(x C.double) C.double {
	xGo := float64(x)
	res := math.Cos(xGo)
	return C.double(res)
}

//
//export Hello1
func Hello1(world string, test string) *C.char {
	res := "Hello1," + world
	fmt.Println(test)
	return C.CString(res)
}

//export Hello2
func Hello2(raw *C.char, size C.int) *C.char {
	data := C.GoBytes(unsafe.Pointer(raw), size)
	dataStr := "Hello2," + string(data)
	return C.CString(dataStr)
}

//export Hello3
func Hello3(test string, raw *C.char, size C.int) *C.char {
	res := "Hello3," + test

	data := C.GoBytes(unsafe.Pointer(raw), size)
	dataStr :=  string(data)
	fmt.Println("Hello3," + dataStr)

	return C.CString(res)
}

func main(){}

关于java中string的传递,可以通过两种方式:

第一种方式:

java中的string,可以编码成为 byte[ ] 数组,根据JNA官方的Type Mapping所述,会映射成为 C中的 一个指针,该指针指向一个 C.char 类型的数组。最后可以借助Cgo,将其转化为go中的 [ ]byte,最后就可以轻易的转化为go中的string。

第二种方式:

把go程序编译成为 native library文件后,会在目录下生成一个 xxx.h 的C 语言头文件:

我们可以看到go中的 string,也就是C中的一个 struct。因此我们可以在java代码中构造一个java的Struct来对应C中的struct。

以此为思路,参见JNA官方文档的 Type Mapping:Structs:

The Java Structure represents a native struct. By default, this type is treated as a pointer to structure (struct *) on the native side when used as a parameter or return value. When used as a structure field, the structure is interpreted as by value. To force the complementary interpretation, the tagging interfaces Structure.ByValue and Structure.ByReference are provided.

The data within a Java Structure is automatically written to native memory just before a native function call with a struct parameter, and automatically read from native memory after the function returns.

 

/* Code generated by cmd/cgo; DO NOT EDIT. */

/* package command-line-arguments */


#line 1 "cgo-builtin-export-prolog"

#include <stddef.h> /* for ptrdiff_t below */

#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif

#endif

/* Start of preamble from import "C" comments.  */




/* End of preamble from import "C" comments.  */


/* Start of boilerplate cgo prologue.  */
#line 1 "cgo-gcc-export-header-prolog"

#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H

typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;

/*
  static assertion to make sure the file is being used on architecture
  at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

#endif

/* End of boilerplate cgo prologue.  */

#ifdef __cplusplus
extern "C" {
#endif


// java:int --> C:int == C.int --> Go:int

extern int Add(int p0, int p1);

// java:double --> C:double == C.double --> Go:float64

extern double Cosine(double p0);

// java:boolean --> C:int == C.int --> Go:int

extern int Boolean(int p0, int p1);

extern char* Hello1(GoString p0, GoString p1);

extern char* Hello2(char* p0, int p1);

extern char* Hello3(GoString p0, char* p1, int p2);

#ifdef __cplusplus
}
#endif

3. 编译go程序

将go程序编译成为 native library文件。

如果是windows系统,则编译命令为:

go build -buildmode=c-shared -o test.dll .\test1.go

如果是linux系统,则编译命令为:

go build -buildmode=c-shared -o libtest.so .\test1.go

 

4. 动态库在java中的映射

首先,我们需要创建一个java中的interface来映射native library,之后我们就可以通过该 interface 的实例来访问native library中的一些函数。

然后为了能映射C中的struct,我们还需要编写一个 java的struct - GoString

 

实例化interface时,引用 .dll文件的 路径 可以设为绝对路径。

import com.sun.jna.*;
import com.sun.jna.Native;
import java.util.Arrays;
import java.util.List;
import java.lang.Math;

public class Client {
    public interface Awesome extends Library {
        // GoString class maps to:
        // C type struct { const char *p; GoInt n; }
        public class GoString extends Structure {
            public static class ByValue extends GoString implements Structure.ByValue {}
            public String p;
            public long n;
            protected List getFieldOrder(){
                return Arrays.asList(new String[]{"p","n"});
            }
        }

        int Add(int a, int b);
        double Cosine(double x);
        boolean Boolean(boolean t, boolean f);
        String Hello1(GoString.ByValue world, GoString.ByValue test);
        String Hello2(byte[] raw, int len);
        String Hello3(GoString.ByValue world, byte[] raw, int len);
    }

    public static void main(String[] args) {
        Awesome awesome = Native.loadLibrary("{dir_path}\\test.dll", Awesome.class);
        // {dir_path}\test.dll
        // {dir_path}/libtest.so

        System.out.println("==========================case1: Add==========================");
        System.out.println(awesome.Add(5, 2));                   // case1: Add
        System.out.println("==========================case2: Cosine==========================");
        System.out.println(awesome.Cosine(Math.PI/3));              // case2: Cosine
        System.out.println("==========================case3: Boolean==========================");
        System.out.println(awesome.Boolean(true, false));         // case3: Boolean

        System.out.println("==========================case4: Hello1==========================");
        // case4: Hello1
        Awesome.GoString.ByValue str1 = new Awesome.GoString.ByValue();
        str1.p = "world1";
        str1.n = str1.p.length();

        Awesome.GoString.ByValue test = new Awesome.GoString.ByValue();
        test.p = "test";
        test.n = test.p.length();
        System.out.println(awesome.Hello1(str1, test));

        // case5: Hello2
        System.out.println("==========================case5: Hello2==========================");
        String str = "world2";
        byte raw[] = str.getBytes();
        System.out.println(awesome.Hello2(raw, raw.length));

        // case6: Hello3
        System.out.println("==========================case6: Hello3==========================");
        Awesome.GoString.ByValue str3 = new Awesome.GoString.ByValue();
        str3.p = "test";
        str3.n = str3.p.length();

        String str4 = "world3";
        byte raw3[] = str4.getBytes();
        System.out.println(awesome.Hello3(str3, raw3, raw3.length));
    }
}

 

输出结果:


==========================case1: Add==========================
7
==========================case2: Cosine==========================
0.5000000000000001
==========================case3: Boolean==========================
java:true --> go: -1
java:false --> go: 0
true
==========================case4: Hello1==========================
test  Hello1,world1
==========================case5: Hello2==========================
Hello2,world2
==========================case6: Hello3==========================
Hello3,world3  test

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值