Java Web項目中使用JNI技術
記錄結構:
JNI技術入門詳解,
注意:手記與接下來要記錄的網站項目中使用JNI技術是無縫連接的。
應用場景:當我們根據不同的平台生成不同的JNI libaray時,例如:linux .so,mac jnilib,windows .dll。我們想在打包web應用時讓程序動態調用c,或者c ++對Java Native Inteface的具體底層實現時,有一種方法是藉助配置在想法中的vm選項中設置庫文件所在的路徑,即-Djava.path.library,剛哥手記最後一部分有說明。
精準定位問題:
那麼有沒有另外一種方式使得Java程序在調用native inteface中抽象本地方法自動載入所需要的代碼呢?也就是說應用程序自動載入.so || (或) .jnilib || .dll?
2.我們知道Java應用程序在調用底層代碼生成的庫文件時,需要指定庫文件所在的路徑。那麼我們的問題就清晰了,問題的痛點在於如何讓應用程序在程序運行期間動態載入庫文件所在的路徑,進而載入所需的庫文件。
網上的一種說法是:在使用的System.loadLibrary( 「具體庫文件所在的路徑的相對路徑」),之前使用System.load(「具體庫文件所在的根目錄的全路徑「),本人試了一下,發現並不起作用。
系統屬性指示JVM在哪裡搜索本機庫。您必須使用-Djava.library.path = / path / to / lib將其指定為JVM參數
,然後當您嘗試使用System.loadLibrary(「foo」)載入庫時
,JVM將搜索庫路徑指定的庫。如果找不到你會得到一個例外:
大致的意思是:
系統屬性 - java.library.path指引JVM去尋找底層的庫文件,你必須為JVM聲明一個屬性,類似於Djava.library.path = / path / to / lib,當你需要使用System.loadLibrary(「foo 「)載入底層foo的庫文件的時候,JVM會按照你聲明的路徑去載入這個庫文件,如果你不聲明的話,會出現下面錯誤:
。這個錯告訴我們富並不庫在我們所要載入的路徑下面
接下來說明原因:
,則不會有任何差異。
源碼中ClassLoader.loadLibrary有這樣一句代碼:
為什麼就定位問題到上述幾行代碼,我們得從源碼的角度來分析,看下源碼:
首先是System.loadLibaray(),藉助想法看下源碼:
/ ** *載入由 libname *參數指定的本機庫。 libname 參數不能包含任何平台 *特定的前綴,文件擴展名或路徑。如果一個 名為 libname 的本地庫與VM靜態鏈接,則 調用庫導出的* JNI_OnLoad_ libname 函數。 *有關詳細信息,請參閱JNI規範。 * *否則,libname參數是從系統庫 *位置載入的,並以 實現方式映射到本機庫映像。 *
*調用 System.loadLibrary(name)有效地 等同於 * * blockquote>
* Runtime.getRuntime()。loadLibrary(name) * * * @param libname庫的名稱。 * @exception SecurityException如果安全管理器存在,並且其 * checkLink 方法不允許 *載入指定的動態庫 * @exception如果libname參數 *包含文件路徑,本地庫不是 與虛擬機靜態*鏈接,或者庫不能被 主機系統映射到*本機庫映像。 * @exception NullPointerException如果 libname 是 * null * @see java.lang.Runtime#loadLibrary(java.lang.String) * @see java.lang.SecurityManager#checkLink (java.lang.String) * / @CallerSensitive public static void loadLibrary(String libname){ Runtime.getRuntime()。loadLibrary0(Reflection.getCallerClass(),libname); }
可以看到 方法調用中出現Runtime.getRuntime()。loadLibrary0(),從這行代碼我們知道庫文件是在運行時被載入起作用的。
我們繼續看loadLibrary0()
/ ** *載入由 libname *參數指定的本機庫。 libname 參數不能包含任何平台 *特定的前綴,文件擴展名或路徑。如果一個 名為 libname 的本地庫與VM靜態鏈接,則 調用庫導出的* JNI_OnLoad_ libname 函數。 *有關詳細信息,請參閱JNI規範。 * *否則,libname參數是從系統庫 *位置載入的,並以 實現方式映射到本機庫映像。 *
*首先,如果有安全管理器, 則使用 libname 作為參數調用其 checkLink *方法。 *這可能會導致安全例外。 *
*方法{@link System#loadLibrary(String)}是傳統的 *方便的方法。如果本機 *方法用於實現一個類,則標準 *策略是將本機代碼放入庫文件(調用它 * LibFile ),然後放入靜態初始化器: *
* static * *在類聲明中。當類被載入和 *初始化時,本機 *方法的必要本機代碼實現也將被載入。 *
*如果使用相同的庫 *名稱多次調用此方法,則將忽略第二個和後續調用。 * * @param libname庫的名稱。 * @exception SecurityException如果安全管理器存在,並且其 * checkLink 方法不允許 *載入指定的動態庫 * @exception如果libname參數 *包含文件路徑,本地庫不是 與虛擬機靜態*鏈接,或者庫不能被 主機系統映射到*本機庫映像。 * @exception NullPointerException如果 libname 是 * null * @see java.lang.SecurityException * @see java.lang.SecurityManager#checkLink(java.lang.String) * / @CallerSensitive public void loadLibrary(String libname){ loadLibrary0(Reflection.getCallerClass(),libname); } synchronized void loadLibrary0(Class fromClass,String libname){ SecurityManager security = System.getSecurityManager(); if(security!= null){ security.checkLink(libname); } if(libname.indexOf((int)File.separatorChar)!= -1){ throw new UnsatisfiedLinkError( 「目錄分隔符不應出現在庫名稱中:」+ libname); } ClassLoader.loadLibrary(fromClass,libname,false); }
題外話:loadLibrary(),loadLibrary0()這兩個方法的命名還是挺不符合規範的,歷史遺留問題吧。
在loadLibrary中我們看到了ClassLoader.loadLibrary(fromClass,libname,false) ;方法
繼續追溯
對於上述代碼的解釋我們可以從這篇博客中獲取到答案:
如果將sys_paths設置為null,則在嘗試載入庫時,庫路徑將被重新初始化。
那麼問題來了,通過剛才的源代碼追溯,我們知道System.loadLibray()調用ClassLoader.loadLibrary ()方法,
我們應該如何將sys_paths設置為空?
通過上述情景描述,我們要更改sys_paths的值為空,只能在sys_paths初始化之前做手腳(反射在程序動態運行期間更改程序中的屬性值)。
代碼如下:
追溯上述代碼,調試結果如下圖所示:
最終程序的正常運行。
我在網路項目中的應用是這樣的:
程序封裝,對JNI的使用封裝成jniutil工具類:
代碼如下:
注意:對庫文件的載入放置在靜態代碼塊中,
記錄完畢。
※Java抽象類使用實例解讀
※Java到底是不是一種純面向對象語言?
※JAVA 對象引用,以及對象賦值
※從零開始學會做一個簡單的APP
TAG:java吧 |
※使用 Angular CLI 搭建項目
※如何在 Emacs 中使用 Magit 管理 Git 項目
※谷歌或重啟Google Glass項目 融入AR技術
※開源項目Safespaces想讓你在VR中使用Linux系統
※Github 項目推薦 用PyTorch 實現 OpenNMT
※GitHub項目 | PyTorch 中文手冊
※springboot項目中使用原生jdbc連接MySQL資料庫
※AT&T聯合SKT和Intel啟動Airship OpenStack項目
※Blazor正式成為Microsoft官方.NET 和WebAssembly項目
※基於SpringBoot的WEB API項目的安全設計
※Lumia手機刷Win10 ARM項目GitHub上線
※對話倫敦時尚商業孵化器 CFE 項目主管 Ishwari Thopte
※即插即用 戴姆勒與Hubject、Ebee合作Plug&Charge充電項目
※使用Jira software+Structure實現大規模跨團隊項目管理
※GitHub 熱門項目:Python Fire
※使用Visual Studio Code編譯、調試Apollo項目
※為刺激VR/AR領域發展 Digital Catapult再次啟動Augmentor項目
※GitHub 熱門項目:PyTorch 資源大全
※Servlet+MyBatis項目轉Spring Cloud微服務,多數據配置修改建議
※VMware 收編 Serverless OpenFaaS 創始人;戴爾關閉開源項目