JNA

JNA(Java Native Access)是建立在JNI(Java Native Interface,Java本地調(diào)用)技術(shù)之上的Java開源框架,JNA提供了一組Java工具類用于在運行期間動態(tài)訪問系統(tǒng)本地庫(Native Library,如Windows的動態(tài)鏈接庫*.dll、Linux的共享庫*.so)。

使用JNA開發(fā)后無需編寫任何Native/JNI代碼,只需在Java接口中描述目標(biāo)Native Library的函數(shù)與結(jié)構(gòu),JNA會自動實現(xiàn)Java接口到Native Library的映射,可以方便地使用Java直接訪問動態(tài)鏈接庫中的函數(shù)。

JNA提供了一個動態(tài)的C編寫的轉(zhuǎn)發(fā)器,可自動實現(xiàn)Java和C的數(shù)據(jù)類型映射。DLL和SO是C函數(shù)的集合和容器,這與Java中的接口概念吻合,JNA把DLL/SO文件看作是接口,在JNA中定義一個接口相當(dāng)于定義了一個DLL/SO文件的描述文件,Java接口代表了動態(tài)鏈接庫中發(fā)布的函數(shù)。

使用JNA一般只適用于簡單的C/C++庫,如果接口、數(shù)據(jù)結(jié)構(gòu)復(fù)雜的化就不推薦,而且JNA也只提供了C/C++對Java的接口轉(zhuǎn)化。

JNI

JNI全稱Java Native Interface即Java本地調(diào)用,從Java1.1開始JNI標(biāo)準(zhǔn)成為Java平臺的一部分,實現(xiàn)了Java代碼和其他語言編寫的代碼交互。JNI實現(xiàn)Java跨平臺的同時,也能與其他語言(如C、C++)的動態(tài)庫交互。

使用JNI可以實現(xiàn)Java程序中的函數(shù)調(diào)用Native語言編寫的函數(shù),Native一般指的是C/C++編寫的函數(shù)。使用JNI可以實現(xiàn)在Native程序的函數(shù)中調(diào)用Java層的函數(shù),也就是在C/C++程序中可以調(diào)用Java函數(shù)。

JVM本身就是用Native語言編寫的,JVM運行在具體平臺上其本身是無法做到與平臺無關(guān)的。通過JNI技術(shù)可以對Java層屏蔽具體JVM實現(xiàn)上的差異,進(jìn)而實現(xiàn)Java本身的平臺無關(guān)性。

使用JNI并不簡單,對一個編譯好的DLL/SO文件,使用JNI調(diào)用時首先需要使用C另外編寫一個DLL/SO共享庫,使用SUN規(guī)定的數(shù)據(jù)結(jié)構(gòu)替代C的數(shù)據(jù)結(jié)構(gòu),再調(diào)用已有的DLL/SO中公布的函數(shù)。然后在Java中載入這個DLL/SO,最后編寫Java Native函數(shù)作為鏈接庫中函數(shù)的代理。使用JNI技術(shù)調(diào)用本地代碼比較繁瑣,因此引入了JNA技術(shù)。

使用JNI調(diào)用C/C++的過程

時序 步驟 文件類型
1 編寫Java類代碼 *.java
2 編譯成字節(jié)碼 *.class
3 產(chǎn)生C/C++頭文件 *.h
4 編寫JNI實現(xiàn)代碼 *.c/cpp
5 編譯成鏈接庫文件 *.dll/so

使用JNA相比JNI調(diào)用動態(tài)鏈接庫會有性能損耗,速度會降低幾倍。另外,使用JNI不僅可以實現(xiàn)Java訪問C函數(shù),也可實現(xiàn)C調(diào)用Java代碼。但JNA只能實現(xiàn)Java訪問C函數(shù)。

入門

JNA定義的接口繼承自com.sun.jna.Library接口,若DLL文件中的函數(shù)以stdcall方式輸出,接口應(yīng)繼承com.sun.jna.win32.StdCallLibrary接口。

例如:在SpringBoot中是用DLL文件

  1. 創(chuàng)建Maven工程,將DLL文件放到resources目錄下。
  2. 引入JNA相關(guān)的jar包
  3. 創(chuàng)建繼承自Library類的接口
  4. 接口中創(chuàng)建對象用于加載DLL/SO的類庫。
  5. 接口中聲明DLL/SO類庫頭文件中暴露的方法

引入依賴

$ vim pom.xml
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>5.4.0</version>
</dependency>

JNA引入動態(tài)鏈接庫

通過JNA建立與動態(tài)鏈接庫的映射,只需創(chuàng)建一個接口來調(diào)用Native的loadLabrary()方法。

JNA建立與動態(tài)鏈接庫中函數(shù)的對應(yīng)關(guān)系,只需在加載相應(yīng)類庫接口中聲明的函數(shù)即可。

JNA調(diào)用原生函數(shù)的模式

JNI中是用Native關(guān)鍵字來聲明一個Java方法表明外部的原生函數(shù),JNA中沒有使用Native表明原生函數(shù)而是使用Java Interface來表明動態(tài)鏈接庫中全部原生函數(shù)。

使用JNI加載動態(tài)鏈接庫時必須使用System.loadLibrary()方法,將專門為JNI編寫的動態(tài)鏈接庫載入,這個動態(tài)鏈接庫實際上是真正動態(tài)鏈接庫的代理。使用JNA無需編寫作為代理的動態(tài)鏈接庫,直接使用JNA庫的Native類中的loadLibrary()方法可直接加載最終的動態(tài)鏈接庫。

類型映射

JNA使用的數(shù)據(jù)類型是Java的數(shù)據(jù)類型,而原生函數(shù)中是用的數(shù)據(jù)類型是原生函數(shù)編程語言的數(shù)據(jù)類型,大多為C/Delphi/匯編等語言的數(shù)據(jù)類型。如果數(shù)據(jù)類型映射不一致,調(diào)用時可能會發(fā)生無法預(yù)知的行為導(dǎo)致調(diào)用失敗。

JNA的難點在于編程語言之間的數(shù)據(jù)類型不一致,Java中是用的函數(shù)必須與鏈接庫中的函數(shù)的函數(shù)原型保持一致,這是JNA甚至是所有跨平臺調(diào)用的難點,因為C/C++的類型與Java的類型是不一樣的,因此必須將其裝換為Java對應(yīng)的數(shù)據(jù)類型,這就是類型映射(Type Mappings)。類型映射的難點在于結(jié)構(gòu)體、指針和函數(shù)回調(diào)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容