由于需要設計一個自動的對Scala和Java項目打包生成的可執(zhí)行的Jar加密的方案,而加密過程是使用C語言所寫柬焕,而加密打包Docker鏡像的過程又在Scala語言的項目里欣硼,因此需要使用JNI的技術調用動態(tài)鏈接庫里邊的函數(shù)骑冗。
在實現(xiàn)的過程中秉宿,由于開發(fā)設備是Mac系統(tǒng),而需要打包加密解密的過程是在Linux系統(tǒng)上運行氯迂,所以最終都是以Linux為目標系統(tǒng)進行編譯和測試践叠,Scala語言沒有明顯的系統(tǒng)差異,而使用C語言所寫的加密解密的動態(tài)鏈接庫是需要編譯為目標操作系統(tǒng)格式的嚼蚀,因此整個項目的測試過程都是在Docker鏡像中完成禁灼,而我們的鏡像又是基于Alpine做的極小鏡像,在穩(wěn)定版3.9的系統(tǒng)中又缺少某些必要的lib驰坊, 因此出現(xiàn)了很多問題匾二。
好在最終測試完成后,編譯拳芙、加密察藐、打包的過程和真正運行所依賴的容器基礎配置相同,不用擔心出現(xiàn)依賴的動態(tài)鏈接庫不是編譯時依賴的某個具體版本舟扎,不需要根據(jù)客戶環(huán)境重新調整的分飞。
Header文件的生成
這一步是一切的基礎,一定要先設計好Class的定義再生成對應的.h文件睹限,如果Class做過修改譬猫,要重新生成讯檐,否則無法調用。不管是Java語言還是Scala語言都可以通過javah 來生成header文件染服,但是Java 8之后的版本可以直接在javac 編譯的過程中同時生成header文件别洪。當時因為第一次生成的Header文件是從Java Class中編譯出來的,后來想直接使用該文件在Scala語言中調用native 的實現(xiàn)柳刮,結果出現(xiàn)了一些Link不滿足的問題挖垛。
Scala語言在什么位置加載該JNI類具體的實現(xiàn)
一開始簡單的認為動態(tài)鏈接庫的加載在任何地方都行,只要在Class的加載之前使用System.loadLibrary 即可秉颗,所以一開始將加載類庫的調用放在了Scala class的伴生對象中痢毒,這個Class就是對應JNI的實現(xiàn)的那個類。然而這樣并不起作用蚕甥,最后還是放到了啟動的Main class中哪替。
為什么會出現(xiàn)unsatisfied link的錯誤,具體是哪里不滿足要求
編譯生成的.h文件里邊的方法signature里邊包含了方法的參數(shù)菇怀、返回類型凭舶、包名、類名爱沟、方法名库快,任意一個對應不上就會出現(xiàn)以上錯誤,前面說過因為是后期實現(xiàn)的Scala的Class 通過@native annotation來對應JNI的實現(xiàn)钥顽,可是忽略了包名的對應,以為只是需要類名和方法相關的參數(shù)完全對應就可以了靠汁,實際上重新通過Javah生成的方法簽名蜂大,發(fā)現(xiàn)最終的方法名里邊包含了這個類完整的包名、類名和方法名蝶怔,中間通過下劃線連接奶浦,比如com_kongming_license_JarEncrypt_encrypt(),這樣才能夠滿足JNI所load的方法和class中的native方法之間的對應踢星。
動態(tài)鏈接庫所依賴的lib版本不匹配的問題
動態(tài)鏈接庫libxxx.so.x.不存在的問題由于Docker環(huán)境的統(tǒng)一性澳叉,已經(jīng)不存在了。最初希望使用本地Linux系統(tǒng)直接編譯的lib應用到生產(chǎn)環(huán)境沐悦,在沒有使用Docker的時候成洗,在客戶機上也需要安裝這些對應版本的動態(tài)鏈接庫,需要額外的部署工作藏否,有一些麻煩瓶殃。將具體的編譯和加密所使用的環(huán)境和生產(chǎn)部署的環(huán)境統(tǒng)一避免了此類問題,但是同時帶來了一些開發(fā)上的不便利性副签,最初是需要將代碼放到某個具有和生產(chǎn)環(huán)境一樣的遠程服務器上進行編譯遥椿,需要根據(jù)服務器的環(huán)境來修改cmakelist.txt 中的一些路徑信息基矮,比如所依賴的jdk的header的路徑,脫離了服務器在本地反而無法正常編譯冠场,換來換取的比較麻煩家浇,通過在本地的Docker鏡像中進行編譯解決了這個問題,再配合上vscode最新的remote workspace 環(huán)境對容器的支持碴裙,可以非常方便的在vscode中進行開發(fā)和編譯钢悲,編譯成的動態(tài)鏈接庫直接可以在生產(chǎn)環(huán)境中使用。
Vscode remote workspace 的驚喜
前邊提到過青团,進行C語言的開發(fā)時譬巫,所編譯的動態(tài)鏈接庫對target系統(tǒng)的不同需要進行不同的編譯,不像JVM語言這種督笆,一處編譯可以處處運行芦昔。使用Mac系統(tǒng)進行開發(fā)距離真實的Linux環(huán)境還是有一些不同,會造成以上一些版本和header文件路徑的差別的問題娃肿,而vscode對遠程開發(fā)的支持非常完美的解決了這個問題咕缎,目前支持ssh、docker容器和WSL三種遠程開發(fā)環(huán)境料扰,在Mac上開發(fā)最簡單的就是選擇一個Docker的基礎鏡像凭豪,在此基礎上build本地的docker鏡像,本地的vscode連接到該鏡像所生成的container中進行遠程的開發(fā)晒杈、編譯嫂伞,和直接在本地開發(fā)沒有太多區(qū)別,可以在container的vscode server中安裝獨立的插件拯钻,也可以打開新的terminal安裝一些所缺少的工具帖努、lib之類。這樣既保證了開發(fā)生產(chǎn)環(huán)境的一致性粪般,又能夠像本地開發(fā)一樣拼余,避免了使用虛擬機等遠程開發(fā)環(huán)境的不便利。
感覺以后可以直接在ipad pro上邊寫程序了亩歹,通過連接遠程的容器或者linux服務器匙监,隨時隨地寫程序。