日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java高级用法之:调用本地方法的利器JNA

發布時間:2024/2/28 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java高级用法之:调用本地方法的利器JNA 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 簡介
  • JNA初探
  • JNA加載native lib的流程
  • 本地方法中的結構體參數
  • 總結

簡介

JAVA是可以調用本地方法的,官方提供的調用方式叫做JNI,全稱叫做java native interface。要想使用JNI,我們需要在JAVA代碼中定義native方法,然后通過javah命令創建C語言的頭文件,接著使用C或者C++語言來實現這個頭文件中的方法,編譯源代碼,最后將編譯后的文件引入到JAVA的classpath中,運行即可。

雖然JAVA官方提供了調用原生方法的方式,但是好像這種方法有點繁瑣,使用起來沒有那么的方便。

那么有沒有更加簡潔的調用本地方法的形式嗎?答案是肯定的,這就是今天要講的JNA。

JNA初探

JNA的全稱是Java Native Access,它為我們提供了一種更加簡單的方式來訪問本地的共享庫資源,如果你使用JNA,那么你只需要編寫相應的java代碼即可,不需要編寫JNI或者本地代碼,非常的方便。

本質上JNA使用的是一個小的JNI library stub,從而能夠動態調用本地方法。

JNA就是一個jar包,目前最新的版本是5.10.0,我們可以像下面這樣引用它:

<dependency><groupId>net.java.dev.jna</groupId><artifactId>jna</artifactId><version>5.10.0</version></dependency>

JNA是一個jar包,它里面除了包含有基本的JAVA class文件之外,還有很多和平臺相關的文件,這些平臺相關的文件夾下面都是libjnidispatch*的庫文件。

可以看到不同的平臺對應著不同的動態庫。

JNA的本質就是將大多數native的方法封裝到jar包中的動態庫中,并且提供了一系列的機制來自動加載這個動態庫。

接下來我們看一個具體使用JNA的例子:

public class JNAUsage {public interface CLibrary extends Library {CLibrary INSTANCE = (CLibrary)Native.load((Platform.isWindows() ? "msvcrt" : "c"),CLibrary.class);void printf(String format, Object... args);}public static void main(String[] args) {CLibrary.INSTANCE.printf("Hello, World\n");for (int i=0;i < args.length;i++) {CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);}} }

這個例子中,我們想要加載系統的c lib,從而使用c lib中的printf方法。

具體做法就是創建一個CLibrary interface,這個interface繼承自Library,然后使用Native.load方法來加載c lib,最后在這個interface中定義要使用的lib中的方法即可。

那么JNA到底是怎么加載native lib的呢?我們一起來看看。

JNA加載native lib的流程

在講解JNA加載native lib之前,我們先回顧一下JNI是怎么加載native lib的呢?

在JNI中,我們首先在java代碼中定義要調用的native方法,然后使用javah命令,創建C的頭文件,然后再使用C或者C++來對這個頭文件進行實現。

接下來最重要的一步就是將生成的動態鏈接庫添加到JAVA的classpath中,從而在JAVA調用native方法的時候,能夠加載到對應的庫文件。

對于上面的JNA的例子來說,直接運行可以得到下面的結果:

Hello, World

我們可以向程序添加JVM參數:-Djna.debug_load=true,從而讓程序能夠輸出一些調試信息,再次運行結果如下所示:

12月 24, 2021 9:16:05 下午 com.sun.jna.Native extractFromResourcePath 信息: Looking in classpath from jdk.internal.loader.ClassLoaders$AppClassLoader@251a69d7 for /com/sun/jna/darwin-aarch64/libjnidispatch.jnilib 12月 24, 2021 9:16:05 下午 com.sun.jna.Native extractFromResourcePath 信息: Found library resource at jar:file:/Users/flydean/.m2/repository/net/java/dev/jna/jna/5.10.0/jna-5.10.0.jar!/com/sun/jna/darwin-aarch64/libjnidispatch.jnilib 12月 24, 2021 9:16:05 下午 com.sun.jna.Native extractFromResourcePath 信息: Extracting library to /Users/flydean/Library/Caches/JNA/temp/jna17752159487359796115.tmp 12月 24, 2021 9:16:05 下午 com.sun.jna.NativeLibrary loadLibrary 信息: Looking for library 'c' 12月 24, 2021 9:16:05 下午 com.sun.jna.NativeLibrary loadLibrary 信息: Adding paths from jna.library.path: null 12月 24, 2021 9:16:05 下午 com.sun.jna.NativeLibrary loadLibrary 信息: Trying libc.dylib 12月 24, 2021 9:16:05 下午 com.sun.jna.NativeLibrary loadLibrary 信息: Found library 'c' at libc.dylib Hello, World

仔細觀察上面的輸出結果,我們可以大概了解JNA的工作流程。JNA的工作流程可以分為兩部分,第一部分是Library Loading,第二部分是Native Library Loading。

兩個部分分別對應的類是com.sun.jna.Native和com.sun.jna.NativeLibrary。

第一部分的Library Loading意思是將jnidispatch這個共享的lib文件加載到System中,加載的順序是這樣的:

  • jna.boot.library.path.
  • 使用System.loadLibrary(java.lang.String)從系統的library path中查找。如果不想從系統libary path中查找,則可以設置jna.nosys=true。
  • 如果從上述路徑中沒有找到,則會調用loadNativeDispatchLibrary將jna.jar中的jnidispatch解壓到本地,然后進行加載。如果不想從classpath中查找,則可以設置jna.noclasspath=true。 如果不想從jna.jar文件中解壓,則可以設置jna.nounpack=true。
  • 如果你的系統對于從jar文件中解壓文件有安全方面的限制,比如SELinux,那么你需要手動將jnidispatch安裝在一個可以訪問的地址,然后使用1或者2的方式來設置加載方式和路徑。
  • 當jnidispatch被加載之后,會設置系統變量 jna.loaded=true,表示jna的lib已經加載完畢。

    默認情況下我們加載的lib文件名字叫jnidispatch,你也可以通過設置jna.boot.library.name來對他進行修改。

    我們看一下loadNativeDispatchLibrary的核心代碼:

    String libName = "/com/sun/jna/" + Platform.RESOURCE_PREFIX + "/" + mappedName;File lib = extractFromResourcePath(libName, Native.class.getClassLoader());if (lib == null) {if (lib == null) {throw new UnsatisfiedLinkError("Could not find JNA native support");}}LOG.log(DEBUG_JNA_LOAD_LEVEL, "Trying {0}", lib.getAbsolutePath());System.setProperty("jnidispatch.path", lib.getAbsolutePath());System.load(lib.getAbsolutePath());jnidispatchPath = lib.getAbsolutePath();

    首先是查找stub lib文件:/com/sun/jna/darwin-aarch64/libjnidispatch.jnilib, 默認情況下這個lib文件是在jna.jar包中的,所以需要調用extractFromResourcePath方法將jar包中的lib文件拷貝到臨時文件中,然后調用System.load方法將其加載。

    第二部分就是調用com.sun.jna.NativeLibrary中的loadLibrary方法來加載JAVA代碼中要加載的lib。

    在loadLibrary的時候有一些搜索路徑的規則如下:

  • jna.library.path,用戶自定義的jna lib的路徑,優先從用戶自定義的路徑中開始查找。
  • jna.platform.library.path, 和platform相關的lib路徑。
  • 如果是在OSX操作系統上,則會去搜索 ~/Library/Frameworks, /Library/Frameworks, 和 /System/Library/Frameworks ,去查詢對應的Frameworks。
  • 最后會去查找Context class loader classpath(classpath或者resource path),具體的格式是${os-prefix}/LIBRARY_FILENAME。如果內容是在jar包中,則會將文件解壓縮至 jna.tmpdir,然后進行加載。
  • 所有的搜索邏輯都放在NativeLibrary的方法loadLibrary中實現的,方法體太長了,這里就不一一列舉了,感興趣的朋友可以自行去探索。

    本地方法中的結構體參數

    如果本地方法傳入的參數是基本類型的話,在JNA中定義該native方法就用基本類型即可。

    但是有時候,本地方法本身的參數是一個結構體類型,這種情況下我們該如何進行處理呢?

    以Windows中的kernel32 library為例,這個lib中有一個GetSystemTime方法,傳入的是一個time結構體。

    我們通過繼承Structure來定義參數的結構體:

    @FieldOrder({ "wYear", "wMonth", "wDayOfWeek", "wDay", "wHour", "wMinute", "wSecond", "wMilliseconds" }) public static class SYSTEMTIME extends Structure {public short wYear;public short wMonth;public short wDayOfWeek;public short wDay;public short wHour;public short wMinute;public short wSecond;public short wMilliseconds; }

    然后定義一個Kernel32的interface:

    public interface Kernel32 extends StdCallLibrary { Kernel32 INSTANCE = (Kernel32)Native.load("kernel32", Kernel32.class); Kernel32 SYNC_INSTANCE = (Kernel32)Native.synchronizedLibrary(INSTANCE);void GetSystemTime(SYSTEMTIME result); }

    最后這樣調用:

    Kernel32 lib = Kernel32.INSTANCE; SYSTEMTIME time = new SYSTEMTIME(); lib.GetSystemTime(time);System.out.println("Today's integer value is " + time.wDay);

    總結

    以上就是JNA的基本使用,有關JNA根據深入的使用,敬請期待后續的文章。

    本文的代碼:https://github.com/ddean2009/learn-java-base-9-to-20.git

    本文已收錄于 http://www.flydean.com/02-jna-overview/

    最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

    歡迎關注我的公眾號:「程序那些事」,懂技術,更懂你!

    總結

    以上是生活随笔為你收集整理的java高级用法之:调用本地方法的利器JNA的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。