前言:
這次的項目經(jīng)歷讓我嘗了一次苦頭,在最后一切順利成功執(zhí)行的時候,就決定要寫下我人生的第一篇博客。
本次項目的動態(tài)庫與正常動態(tài)庫不同的2個情況是:一是動態(tài)庫dll內(nèi)部需要調(diào)用一個與dll在同級目錄下的一個配置文件;二是動態(tài)庫在運行過程中會向動態(tài)庫dll同級目錄下創(chuàng)建并寫入一個日志文件(我這邊說的同級目錄可能不太準(zhǔn)確顷歌,具體后文會提到)。
</br>
一幔睬、調(diào)用方式:
JAVA調(diào)用C/C++動態(tài)庫有很多方法眯漩,常用的有JNI(Java Native Interface)、JNA(Java Native Access)溪窒。
- JNI:早在JAVA1.1版本就開始支持坤塞,它定義了一種公用的語法,當(dāng)java和c/c++雙方都遵循該語法時澈蚌,可以互相調(diào)用摹芙。所以使用JNI不能直接調(diào)用一般的C/C++庫,而必須借助于一個中間動態(tài)庫宛瞄,該中間動態(tài)庫實現(xiàn)了JAVA-JNI語法-C/C++的轉(zhuǎn)換(或者你所調(diào)用的動態(tài)庫原生就封裝了JNI)浮禾。如果對C++稍微懂一點,其實使用起來也不難份汗。
- JNA:在C#中盈电,使用[DLLImport]可以非常方便的調(diào)用原生的C/C++動態(tài)庫,所以sun公司開發(fā)了JNA來使JAVA可以像C#一樣方便的調(diào)用動態(tài)庫杯活。不過在使用過程中感覺內(nèi)部原理還是使用了JNI的語法庫匆帚。不過至少對于我們不懂C++的來說,可以直接調(diào)用原生的動態(tài)庫旁钧,而不需要再去生成一個C++的中間動態(tài)庫了吸重。
所以這次在嘗試了JNI之后還是選擇了JNA。
</br>
二歪今、關(guān)于x32和x64:
如果C/C++動態(tài)庫使用x32嚎幸,那必須運行在x32的Tomcat上,并且使用x32的JRE寄猩。所以如果不幸拿到股東動態(tài)庫嫉晶,未提供x64版本,那只能單獨部署x32服務(wù)器提供對應(yīng)的接口田篇,而x64主站點通過http方式調(diào)用該接口程序來訪問動態(tài)庫替废。
如果在x64Tomcat上加載x32版本動態(tài)庫,將得到如下錯誤信息:
"Can't load IA 32-bit .dll on a AMD 64-bit platform"
調(diào)用方法及DLL存放位置:
先來簡單的看一下JNI和JNA兩種方式加載動態(tài)庫的代碼:
JNI:
public class ImportDllTest {
//加載動態(tài)庫
static{
//通過決定地址加載動態(tài)庫
//System.load("d:\\C2JavaTest.dll");
//通過庫名加載動態(tài)庫斯辰,不加.dll后綴舶担,自適應(yīng).dll和liunx平臺的.so
System.loadLibrary("C2JavaTest");
}
//定義動態(tài)庫接口方法
public native String subString(String str, int startIndex, int length);
public static void main(String[] args){
ImportDllTest importDll = new ImportDllTest();
String str = importDll.subString("test",1,2);
}
}
JNA:
引用JNA包
<dependency>
<groupId>com.sun.jna</groupId>
<artifactId>jna</artifactId>
<version>3.0.9</version>
</dependency>
定義接口類,繼承com.sun.jna.Library
//必須繼承com.sun.jna.Library
public interface ImportDllTest extends Library {
//加載動態(tài)庫彬呻,使用動態(tài)庫名稱加載衣陶,不帶.dll后綴,會自動識別liunx環(huán)境下的.so庫闸氮。也可以使用決定地址加載動態(tài)庫
public static ImportDllTest Instance = (ImportDllTest) Native.loadLibrary("C2JavaTest", ImportDllTest.class);
//定義動態(tài)庫接口方法
String subString(String str, int startIndex, int length);
public class Main{
public static void main(String[] args){
String str = ImportDllTest.Instance.subString("test",1,2);
}
}
}
關(guān)于是否加載到動態(tài)庫:
兩種加載動態(tài)庫都可以使用動態(tài)庫名稱或者絕對路徑來加載剪况,在調(diào)試過程中,JNA如果沒找到動態(tài)庫并不會給出明確的提示蒲跨,而JNI的加載方法會明確拋出找不到動態(tài)庫的異常译断,所以即使是使用JNA方式,但在一開始不確定是否將動態(tài)庫放在了正確位置或悲,是否成功的加載了動態(tài)庫的時候可以借助于JNI的Systetm.loadLibrary來協(xié)助判斷是否成功的加載了對應(yīng)的動態(tài)庫孙咪。
</br>
三堪唐、動態(tài)庫位置:
加載動態(tài)庫都可以使用決定地址來加載。但本次項目加載的動態(tài)庫會需要調(diào)用同級目錄下的配置文件翎蹈,使用絕對地址的方式我是沒能成功的加載到配置文件(因為也不清楚動態(tài)庫里實際加載配置文件的具體實現(xiàn))淮菠。使用動態(tài)庫名稱加載,那動態(tài)庫到底應(yīng)該放在哪里荤堪,網(wǎng)上查了很多資料合陵,有放system32的,有放jdk/jre的澄阳,有放tomcat里的拥知,有放環(huán)境變量配置里的。在追求盡量不給后續(xù)運維造成太大困擾(放系統(tǒng)system32碎赢,或jdk/jre低剔,部署時很容易忘記或錯亂),盡可能找最優(yōu)的方案肮塞。
最后我借助于動態(tài)庫生成的日志文件户侥,來判斷默認(rèn)加載動態(tài)庫的路徑:
應(yīng)用程序:
Main方法測試時,動態(tài)庫及配置文件需要放到運行時選擇的工作目錄下峦嗤。
動態(tài)庫和配置文件要放在:
同時生成的日志文件也將會在該目錄下
Tomcat部署程序
Tomcat部署時蕊唐,嘗試過很多方式,但是最后選擇了Tomcat的bin目錄
cd Tomcat/bin
</br>
四烁设、JAVA與C/C++參數(shù)對應(yīng)
java的char是2字節(jié)替梨,byte是1字節(jié);c/c++的char是1字節(jié)装黑;
作為JAVA傳入C/C++參數(shù):
JAVA | C/C++ |
---|---|
byte[] | char[]/char* |
String | char[]/char* |
int | int |
作為JAVA中傳入副瀑,C/C++里out的參數(shù):
C/C++ | JAVA |
---|---|
char[]/char* | byte[] |
調(diào)用是要先將定義的byte數(shù)組空間定義好,c++中才可以在已經(jīng)定義的空間中寫值恋谭。
//out參數(shù)長度8字節(jié)內(nèi)容
byte[] out_param = new byte[8];
</br>
五糠睡、總結(jié)
- 可以借助于JIN確定動態(tài)庫是否能正確加載到。如果動態(tài)庫不需要配置文件疚颊,完全可以使用決定路徑來進行加載狈孔。
- 明確動態(tài)庫的版本,是x32還是x64材义。不同版本要使用對應(yīng)的tomcat和jre均抽。
- 確定傳遞參數(shù)類型,java中一定要記得不可以使用char來和c++的char交互其掂,一定是要byte或String(為什么String可以油挥,猜測可能JIN或JNA在內(nèi)部轉(zhuǎn)換成了byte)。
- 最后如果動態(tài)庫能有人員配合一起調(diào)試,那是一個美好的事情深寥。
</br>
</br>
這次把成功的幾個關(guān)鍵點調(diào)用整理在此攘乒。本次項目也是因為調(diào)用的古董動態(tài)庫,沒有文檔惋鹅,沒有錯誤說明持灰,所有返回都靠猜測,所以不確實是java調(diào)用問題還是本身業(yè)務(wù)問題负饲。前前后后折騰好幾周,最后總算還是有了一個好的結(jié)果喂链。
以上有任何不對的地方返十,敬請指正。
</br>
</br>