参考文档:

 

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的关系,这些关系也将会在后面的case中进行测试:

NativeMappedNativeLongPointerjna.encodingStringor explicitlyor explicitly

然后基于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:

Structurestructstruct *Structure.ByValueStructure.ByReference
Structure

 

/* 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