JNA

JNA(Java Native Access)是建立在JNI(Java Native Interface,Java本地调用)技术之上的Java开源框架,JNA提供了一组Java工具类用于在运行期间动态访问系统本地库(Native Library,如Windows的动态链接库*.dll、Linux的共享库*.so)。

使用JNA开发后无需编写任何Native/JNI代码,只需在Java接口中描述目标Native Library的函数与结构,JNA会自动实现Java接口到Native Library的映射,可以方便地使用Java直接访问动态链接库中的函数。

JNA提供了一个动态的C编写的转发器,可自动实现Java和C的数据类型映射。DLL和SO是C函数的集合和容器,这与Java中的接口概念吻合,JNA把DLL/SO文件看作是接口,在JNA中定义一个接口相当于定义了一个DLL/SO文件的描述文件,Java接口代表了动态链接库中发布的函数。

使用JNA一般只适用于简单的C/C++库,如果接口、数据结构复杂的化就不推荐,而且JNA也只提供了C/C++对Java的接口转化。

  • Github地址 https://github.com/java-native-access/jna
  • API文档 http://java-native-access.github.io/jna/5.2.0/javadoc/

JNI

JNI全称Java Native Interface即Java本地调用,从Java1.1开始JNI标准成为Java平台的一部分,实现了Java代码和其他语言编写的代码交互。JNI实现Java跨平台的同时,也能与其他语言(如C、C++)的动态库交互。

使用JNI可以实现Java程序中的函数调用Native语言编写的函数,Native一般指的是C/C++编写的函数。使用JNI可以实现在Native程序的函数中调用Java层的函数,也就是在C/C++程序中可以调用Java函数。

JVM本身就是用Native语言编写的,JVM运行在具体平台上其本身是无法做到与平台无关的。通过JNI技术可以对Java层屏蔽具体JVM实现上的差异,进而实现Java本身的平台无关性。

使用JNI并不简单,对一个编译好的DLL/SO文件,使用JNI调用时首先需要使用C另外编写一个DLL/SO共享库,使用SUN规定的数据结构替代C的数据结构,再调用已有的DLL/SO中公布的函数。然后在Java中载入这个DLL/SO,最后编写Java Native函数作为链接库中函数的代理。使用JNI技术调用本地代码比较繁琐,因此引入了JNA技术。

使用JNI调用C/C++的过程

时序 步骤 文件类型
1 编写Java类代码 *.java
2 编译成字节码 *.class
3 产生C/C++头文件 *.h
4 编写JNI实现代码 *.c/cpp
5 编译成链接库文件 *.dll/so

使用JNA相比JNI调用动态链接库会有性能损耗,速度会降低几倍。另外,使用JNI不仅可以实现Java访问C函数,也可实现C调用Java代码。但JNA只能实现Java访问C函数。

入门

JNA定义的接口继承自com.sun.jna.Library接口,若DLL文件中的函数以stdcall方式输出,接口应继承com.sun.jna.win32.StdCallLibrary接口。

例如:在SpringBoot中是用DLL文件

  1. 创建Maven工程,将DLL文件放到resources目录下。
  2. 引入JNA相关的jar包
  3. 创建继承自Library类的接口
  4. 接口中创建对象用于加载DLL/SO的类库。
  5. 接口中声明DLL/SO类库头文件中暴露的方法

引入依赖

$ vim pom.xml

    net.java.dev.jna
    jna
    5.4.0

JNA引入动态链接库

通过JNA建立与动态链接库的映射,只需创建一个接口来调用Native的loadLabrary()方法。

JNA建立与动态链接库中函数的对应关系,只需在加载相应类库接口中声明的函数即可。

JNA调用原生函数的模式

JNI中是用Native关键字来声明一个Java方法表明外部的原生函数,JNA中没有使用Native表明原生函数而是使用Java Interface来表明动态链接库中全部原生函数。

使用JNI加载动态链接库时必须使用System.loadLibrary()方法,将专门为JNI编写的动态链接库载入,这个动态链接库实际上是真正动态链接库的代理。使用JNA无需编写作为代理的动态链接库,直接使用JNA库的Native类中的loadLibrary()方法可直接加载最终的动态链接库。

类型映射

JNA使用的数据类型是Java的数据类型,而原生函数中是用的数据类型是原生函数编程语言的数据类型,大多为C/Delphi/汇编等语言的数据类型。如果数据类型映射不一致,调用时可能会发生无法预知的行为导致调用失败。

JNA的难点在于编程语言之间的数据类型不一致,Java中是用的函数必须与链接库中的函数的函数原型保持一致,这是JNA甚至是所有跨平台调用的难点,因为C/C++的类型与Java的类型是不一样的,因此必须将其装换为Java对应的数据类型,这就是类型映射(Type Mappings)。类型映射的难点在于结构体、指针和函数回调。

  • https://github.com/java-native-access/jna/blob/master/www/Mappings.md

你可能感兴趣的:(JNA)