javaee項(xiàng)目進(jìn)行JNI開發(fā)

前言

jni是什么相信很多人都了解了实辑,這里也不多做解釋圾旨。這篇文章主要介紹在進(jìn)行javaweb或者javaee服務(wù)器開發(fā)時(shí)如何進(jìn)行JNI的開發(fā)以及如何正確引入第三方提供的so庫(kù)

環(huán)境

  • linux centos6.8
  • gcc 4.4.7
  • java "1.8.0_171"

一 在linux環(huán)境進(jìn)行簡(jiǎn)單的JNI開發(fā)

1.編寫一個(gè)簡(jiǎn)單的java程序

進(jìn)入任意一個(gè)工作目錄,我這里是/home/ctest/java/

[root@centos68 java]# vim JNITest.java

//java類代碼
    public class JNITest {
 
    public static void main(String[] args){
        System.out.println("hello wrold");
    }
}

//保存并退出

#進(jìn)行編譯
[root@centos68 java]# javac JNITest.java
//編譯后再目錄下應(yīng)該可以看到JNITest.class
[root@centos68 java]# ls
JNITest.class   JNITest.java

#運(yùn)行java程序(這里不要寫JNITest.class)
[root@centos68 java]# java JNITest
hello wrold

//可以看到正確輸出了hello world

上面的這個(gè)簡(jiǎn)單的java程序是為了驗(yàn)證你的機(jī)器已經(jīng)正確配置了jdk環(huán)境變量

2.加入本地方法

//對(duì)上面那個(gè)JNITest.java進(jìn)行修改
public class JNITest {
   static {
        //對(duì)應(yīng)的庫(kù)名稱是libhello.so
        System.loadLibrary("hello");
    }

    public static void main(String[] args){
        System.out.println(getStringFromJNI());
    }

    /**
     * 添加本地方法  返回一個(gè)字符串
     * @return
     */
    public native static String getStringFromJNI();
}

上面這段代碼我們引入了一個(gè)本地方法getStringFromJNI() 這個(gè)方法的實(shí)現(xiàn)在本地庫(kù)libhello.so, 下面我們?nèi)ゾ帉戇@個(gè)庫(kù)

  • 使用javah生成頭文件hello.h
[root@centos68 java]# javah -o hello.h -classpath . -jni JNITest
[root@centos68 java]# ls
hello.h  JNITest.class  JNITest.java

查看一下這個(gè)hello.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JNITest */

#ifndef _Included_JNITest
#define _Included_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JNITest
 * Method:    getStringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_JNITest_getStringFromJNI
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

  • 源文件hello.c編寫
#include "JNITest.h"

JNIEXPORT jstring JNICALL Java_JNITest_getStringFromJNI
  (JNIEnv * env, jclass jcls)
{
        return (*env)->NewStringUTF(env,"string from jni");
}
  • 編譯所需動(dòng)態(tài)庫(kù)libhello.so

//下面這段編譯參數(shù)
*-fPIC 表示生成的動(dòng)態(tài)庫(kù)不要包含地址信息(主要是指一些內(nèi)存地址馒闷,如果包含了地址信息攘滩,那么在不同的進(jìn)程加載的該庫(kù)無(wú)法實(shí)現(xiàn)真正的內(nèi)存共享,而是會(huì)做一份拷貝)
*-I 表示到指定的目錄下尋找頭文件 因?yàn)槲覀円肓? jni.h   這個(gè)頭文件所在的目錄是在$JAVA_HOME/include/中   所以要告訴gcc編譯器到這個(gè)目錄下尋找頭文件  多個(gè)-I 可以指定多個(gè)檢索目錄
* -shared 表示要生成的是動(dòng)態(tài)庫(kù)
* -o 指定生成的動(dòng)態(tài)庫(kù)名字  這里注意必須是libxxxxx.so格式

[root@centos68 java]# gcc -fPIC -I $JAVA_HOME/include/ -I $JAVA_HOME/include/linux/ -shared -o libhello.so hello.c                                                
[root@centos68 java]# ls
hello.c  hello.h  JNITest.class JNITest.java  libhello.so

#運(yùn)行后可以看到生成了libhello.so

  • 運(yùn)行測(cè)試
//我們?cè)囍幾g運(yùn)行一下JNITEst.java
[root@centos68 java]# javac JNITEst.java
[root@centos68 java]# java JNITest
Exception in thread "main" java.lang.UnsatisfiedLinkError: no hello in java.library.path
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
        at java.lang.Runtime.loadLibrary0(Runtime.java:870)
        at java.lang.System.loadLibrary(System.java:1122)
        at JNITest.<clinit>(JNITest.java:4)
        
        
//可以看到這邊報(bào)錯(cuò)了   報(bào)錯(cuò)的原因是hello這個(gè)庫(kù)不在java.library.path這個(gè)目錄下 那應(yīng)該如何才能運(yùn)行呢?

方法一:顯示指定java.library.path的值告訴jvm啟動(dòng)的時(shí)候可以在這個(gè)目錄下搜索動(dòng)態(tài)庫(kù)

//.表示當(dāng)前目錄
[root@centos68 java]# java -Djava.library.path=. JNITest
string from jni

方法二: 配置環(huán)境變量LD_LIBRARY_PATH

LD_LIBRARY_PATH環(huán)境變量用于在程序加載運(yùn)行期間查找動(dòng)態(tài)鏈接庫(kù)時(shí)指定除了系統(tǒng)默認(rèn)路徑之外的其他路徑走芋。LD_LIBRARY_PATH中指定的路徑會(huì)在系統(tǒng)默認(rèn)路徑之前進(jìn)行查找。

臨時(shí)生效 在當(dāng)前shell設(shè)置環(huán)境變量值LD_LIBRARY_PATH

[root@centos68 java]# export LD_LIBRARY_PATH=.
[root@centos68 java]# java JNITest
string from jni

永久生效 在~/.bashrc 或者/etc/profile中添加環(huán)境變量LD_LIBRARY_PATH

//這邊為了讓所有用戶都有效  可以在/etc/profile中添加

[root@centos68 java]# vim /etc/profile 

//添加如下代碼    可以在export PATH 下方添加
LD_LIBRARY_PATH=/home/ctest/java
export LD_LIBRARY_PATH

//使修改生效
[root@centos68 java]# source /etc/profile 

//運(yùn)行
[root@centos68 java]# java JNITest
string from jni

方法三:/etc/ld.so.conf中指定動(dòng)態(tài)庫(kù)搜索路徑

在linux系統(tǒng)中 ldconfig程序用于從磁盤預(yù)裝動(dòng)態(tài)庫(kù)到內(nèi)存中潘鲫,這樣可以提高程序運(yùn)行時(shí)對(duì)共享庫(kù)的調(diào)用速度,ldconfig搜索路徑在/etc/ld.so.conf文件中配置翁逞,每一行指定一個(gè)搜索路徑,可以在該文件下添加一行/home/ctest/java溉仑,然后調(diào)用ldconfig重新去裝載動(dòng)態(tài)庫(kù)

[root@centos68 java]# vim /etc/ld.so.conf

//添加一行/home/ctest/java

#重新裝載動(dòng)態(tài)庫(kù)
[root@centos68 java]# ldconfig

注意: 這種配置方式是基于linux系統(tǒng)的挖函,在純c開發(fā)中測(cè)試沒有問題,但在java調(diào)用中并沒有起作用浊竟,雖然通過ldconfig -p | grep libhello.so 可以查看到動(dòng)態(tài)庫(kù)已經(jīng)被裝載怨喘,但是運(yùn)行java JNITest 還是報(bào)錯(cuò),不知道是為什么逐沙。

方法四:將動(dòng)態(tài)庫(kù)添加到系統(tǒng)默認(rèn)搜索動(dòng)態(tài)庫(kù)的路徑下哲思,例如/lib,/usr/lib 64位系統(tǒng)是/lib64,/usr/lib64

動(dòng)態(tài)庫(kù)的搜索路徑搜索的先后順序是:


1.編譯目標(biāo)代碼時(shí)指定的動(dòng)態(tài)庫(kù)搜索路徑洼畅;(這是針對(duì)c或者c++可執(zhí)行程序而言吩案,在編譯時(shí)通過添加編譯參數(shù)-Wl,-rpath=.來(lái)指定運(yùn)行時(shí)動(dòng)態(tài)鏈接的搜索路徑)

2.環(huán)境變量LD_LIBRARY_PATH指定的動(dòng)態(tài)庫(kù)搜索路徑;

3.配置文件/etc/ld.so.conf中指定的動(dòng)態(tài)庫(kù)搜索路徑帝簇;

4.默認(rèn)的動(dòng)態(tài)庫(kù)搜索路徑/lib徘郭;

5.默認(rèn)的動(dòng)態(tài)庫(kù)搜索路徑/usr/lib。

引入第三方so庫(kù)

其實(shí)通過上面那個(gè)例子 大家應(yīng)該已經(jīng)可以知道怎么去添加第三方編寫好的so庫(kù)了

這里我們以騰訊TLS后臺(tái)api接口引入作為測(cè)試

文檔地址

TLS后臺(tái)API

下載資源包 tls_sig_api-linux-64 因?yàn)槭窃票P下載 所以先下載到window電腦再通過ftp傳到服務(wù)器

解壓并拷貝所需要的文件:

tls_sig_api-linux-64\tls_sig_api-linux-64\example\java

文件夾和

tls_sig_api-linux-64\tls_sig_api-linux-64\lib\jni\jnisigcheck.so

以及
tls_sig_api-linux-64\tls_sig_api-linux-64\java\tls_sigcheck.java

以上java文件夾丧肴,so庫(kù)残揉,和tls_sigcheck.java通過ftp上傳到服務(wù)器

1.將類tls_sigcheck.java放到com/tls/sigcheck下 刪除tls_sigcheck.class 因?yàn)槲覀円薷倪@個(gè)類

2.修改jnisigcheck.so 為 libjnisigcheck.so

[root@centos68 tls]# pwd
/home/ctest/java/tls
[root@centos68 tls]# ls
com  Demo.class  Demo.java  ec_key.pem  jnisigcheck.so  public.pem  README
[root@centos68 tls]# mv jnisigcheck.so libjnisigcheck.so 
[root@centos68 tls]# ls
com  Demo.class  Demo.java  ec_key.pem  libjnisigcheck.so  public.pem  README
[root@centos68 tls]# rm -rf Demo.class 
[root@centos68 tls]# ls
com  Demo.java  ec_key.pem  libjnisigcheck.so  public.pem  README
[root@centos68 tls]# 

修改tls_sigcheck.java

 /**
     * @param libPath 動(dòng)態(tài)庫(kù)的絕對(duì)路徑
     * 加載動(dòng)態(tài)庫(kù)
     */
 //   public void loadJniLib(String libPath) {
 //       System.load(libPath);
 //   }

#注釋上方的代碼   添加如下代碼  我們使用相對(duì)路徑加載動(dòng)態(tài)庫(kù)libjnisigcheck.so

 /**
     * @param libPath 動(dòng)態(tài)庫(kù)的絕對(duì)路徑
     * 加載動(dòng)態(tài)庫(kù)
     */
    static {
        System.loadLibrary("jnisigcheck");
    }

修改Demo.java


// 使用前請(qǐng)修改動(dòng)態(tài)庫(kù)的加載路徑
#注釋下面兩行代碼  因?yàn)槲覀冊(cè)趖ls_sigcheck.java中使用了靜態(tài)代碼塊加載動(dòng)態(tài)庫(kù),因此這里不需要再調(diào)用加載
// demo.loadJniLib("D:\\src\\oicq64\\tinyid\\tls_sig_api\\windows\\64\\lib\\jni\\jnisigcheck.dll");
//demo.loadJniLib("/home/tls/tls_sig_api/src/jnisigcheck.so");

重新編譯并運(yùn)行

[root@centos68 tls]# javac Demo.java 
[root@centos68 tls]# ls
com  Demo.class  Demo.java  ec_key.pem  libjnisigcheck.so  public.pem  README
[root@centos68 tls]# java -Djava.library.path=. Demo
sig:
eJxlj1FPgzAUhd-5FYRXjGnL2m0mPiybTgyiDF2iL6SuLakM6NpCtxj-uxGXSOJ9-b6cc*6n5-t*8Jzkl3S3a7vGFvakeOBf*QEILv6gUpIV1BaRZv8gPyqpeUGF5XqAEGOMABg7kvHGSiHPxlHS9qNrRoJhVTG0-CZMwM8hMh0rshzgw83LMs6WVZ0*ancXJnmfEH1bp6QyoJy9hnR7jyvxtl1PI8eUzlaLuMwdMH27D4FYQxFDglq1oocnW3eTzSHevO*dzhYmdY6461GllTU-v4QwmQM4m49oz7WRbTMICEAMUTTMDrwv7xtapl6r
--
verify ok -- expire time 15552000 -- init time 1525690189
[root@centos68 tls]# 

需要注意的是:一般帶有native方法的類 比如上面的tls_sigcheck.java 它所在的包路徑是固定的 必須按照官方建議的組織結(jié)構(gòu)進(jìn)行放置芋浮, 比如這邊是在com.tls.sigcheck這個(gè)包下 抱环, 這是因?yàn)樵趕o庫(kù)中所定義的方法名稱包含有包信息 不能變更

通過上面的運(yùn)行我們成功的跑起了demo并且生成了需要的sig 在正式項(xiàng)目中 在正式項(xiàng)目中,可以將so文件通過上述幾種方式加入到系統(tǒng)運(yùn)行時(shí)的搜索路徑下,實(shí)現(xiàn)動(dòng)態(tài)加載镇草。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末眶痰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子梯啤,更是在濱河造成了極大的恐慌希痴,老刑警劉巖州胳,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡宰缤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門毒坛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蚪缀,“玉大人,你說(shuō)我怎么就攤上這事杭棵』楸梗” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵魂爪,是天一觀的道長(zhǎng)先舷。 經(jīng)常有香客問我,道長(zhǎng)滓侍,這世上最難降的妖魔是什么蒋川? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮撩笆,結(jié)果婚禮上捺球,老公的妹妹穿的比我還像新娘。我一直安慰自己夕冲,他們只是感情好氮兵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著歹鱼,像睡著了一般泣栈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弥姻,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天南片,我揣著相機(jī)與錄音,去河邊找鬼庭敦。 笑死疼进,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的秧廉。 我是一名探鬼主播伞广,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼拣帽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了嚼锄?” 一聲冷哼從身側(cè)響起诞外,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎灾票,沒想到半個(gè)月后峡谊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡刊苍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年既们,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片正什。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡啥纸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出婴氮,到底是詐尸還是另有隱情斯棒,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布主经,位于F島的核電站荣暮,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏罩驻。R本人自食惡果不足惜穗酥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惠遏。 院中可真熱鬧砾跃,春花似錦、人聲如沸节吮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)透绩。三九已至翘骂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間渺贤,已是汗流浹背雏胃。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工请毛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留志鞍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓方仿,卻偏偏與公主長(zhǎng)得像固棚,于是被迫代替她去往敵國(guó)和親统翩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 一此洲,apk以進(jìn)程的形式運(yùn)行厂汗,進(jìn)程的創(chuàng)建是由zygote。 參考文章《深入理解Dalvik虛擬機(jī)- Android應(yīng)...
    Kevin_Junbaozi閱讀 2,840評(píng)論 0 12
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,070評(píng)論 25 707
  • 非常非常喜歡秋天呜师,即使天氣干燥娶桦,刮來(lái)的風(fēng)讓臉上一整天都是緊繃的感覺,但是這都不影響我對(duì)這個(gè)季節(jié)的喜歡汁汗。 秋天的菜園...
    溫妮小世界閱讀 277評(píng)論 0 0
  • 剛開學(xué)衷畦,果然不適應(yīng),更何況有那么些棘手的事知牌。 文章要改祈争,課題要開,學(xué)問還得接著做角寸。 想想美好的事菩混,或者沉浸于自己的...
    鷹王守仁閱讀 161評(píng)論 0 1
  • 吳京《戰(zhàn)狼2》50億慶功宴只有兩明星到場(chǎng),真正的原因讓人心酸:在過去的這一個(gè)月扁藕,電影《戰(zhàn)狼2》的票房和口碑直線上升...
    可耐的牛牛閱讀 203評(píng)論 0 0