参考文档:
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 Type | Native Representation | Java Type |
---|---|---|
char | 8-bit integer | byte |
int | 32-bit integer | int |
int | boolean flag | boolean |
float | 32-bit floating point | float |
double | 64-bit floating point | double |
pointer (e.g. void*), array |
32- or 64-bit pointer to memory (argument/return) contiguous memory (struct member) |
[] (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
以下编写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 /* 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
将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
首先,我们需要创建一个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