Linux編譯動(dòng)態(tài)鏈接庫(kù),springboot集成調(diào)用so方法

1.安裝編譯環(huán)境

yum -y install gcc  
yum -y install gcc-c++  

yum install -y java-1.8.0-openjdk*

2.編譯C動(dòng)態(tài)庫(kù)

準(zhǔn)備三個(gè)文件:hello.h 谬墙、 hello.c 配并、 main.c

//hello.h
#ifndef _HELLO_H_
#define _HELLO_H_

void hello();

#endif  /* _HELLO_H_ */

//hello.c
#include <stdio.h>
#include "hello.h"
 
void  hello()
{
    printf("這是動(dòng)態(tài)鏈接庫(kù)接口方法\n");
}

//main.c
#include <stdio.h>
#include "hello.h"
 
int main(void)
{
    hello();
    return 0;
}

2.1 編譯生成so

gcc hello.c  -fPIC -shared -o libnative.so

參數(shù)說(shuō)明:
    -fPIC 位置無(wú)關(guān)碼
    -shared 按照共享庫(kù)的方式來(lái)鏈接

2.2 可執(zhí)行程序鏈接so

gcc main.c -L. -lnative -o main

參數(shù)說(shuō)明:
    -L參數(shù):指明要鏈接的so庫(kù)所在路徑(如-L. 表示當(dāng)前路徑邦泄, -L../so 表示當(dāng)前路徑的上一層目錄的so子文件夾中)
    -l參數(shù):指明要連接的庫(kù)的名字,如-lnative 表示要鏈接libnative.so庫(kù)

2.3運(yùn)行可執(zhí)行程序

./main

注意:運(yùn)行的時(shí)候會(huì)提示找不到鏈接庫(kù),需要配置系統(tǒng)鏈接庫(kù)的位置

配置系統(tǒng)環(huán)境變量:

//當(dāng)前窗口有效
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/root/native  

//永久生效
vim /etc/profile
export LD_LIBRARY_PATH=/root/native
source /etc/profile

3.SpringBoot調(diào)用Jni動(dòng)態(tài)庫(kù)

  • 準(zhǔn)備源碼文件:HelloJNI.java、HelloNative.h烈掠、HelloNative.c
//HelloJNI.java
public class HelloJNI {
    //鏈接庫(kù)的方法
    public native static void setNum(int num);  
    public native static int get();
}
//HelloNative.h
#include <jni.h>
#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT void JNICALL Java_com_start_printer_HelloJNI_setNum
  (JNIEnv *, jclass, jint);

JNIEXPORT jint JNICALL Java_com_start_printer_HelloJNI_get
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif


//HelloNative.c
#include "HelloNative.h"
int result=888; 

JNIEXPORT void JNICALL Java_com_start_printer_HelloJNI_setNum(JNIEnv * env, jclass jc, jint num)
{
    result+=num;
}

JNIEXPORT jint JNICALL Java_com_start_printer_HelloJNI_get(JNIEnv * env, jclass jc)
{
    return result;
}

  • 配置jdk環(huán)境變量:
vim /etc/profile
JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-3.b13.el7_5.x86_64
PATH=$PATH:$JAVA_HOME/bin  
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar  
export JAVA_HOME  CLASSPATH  PATH

3.1 編譯生成jni的so庫(kù)

 gcc HelloNative.c -fPIC -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -shared -o libHelloNative.so 

3.2 集成到springboot工程

  • 拷貝HelloJNI.java文件到j(luò)ni接口聲明的包中
  • 拷貝so文件到resouces/native文件夾下
  • 動(dòng)態(tài)加載so類:
public class NativeLoader {
    /**
     * 加載項(xiàng)目下的native文件,DLL或SO
     *
     * @param dirPath 需要掃描的文件路徑缸托,項(xiàng)目下的相對(duì)路徑
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public synchronized static void loader(String dirPath) throws IOException, ClassNotFoundException {
        Enumeration<URL> dir = Thread.currentThread().getContextClassLoader().getResources(dirPath);
        // 獲取操作系統(tǒng)類型
        String systemType = System.getProperty("os.name");
        //String systemArch = System.getProperty("os.arch");
        // 獲取動(dòng)態(tài)鏈接庫(kù)后綴名
        String ext = (systemType.toLowerCase().indexOf("win") != -1) ? ".dll" : ".so";
        while (dir.hasMoreElements()) {
            URL url = dir.nextElement();
            String protocol = url.getProtocol();
            if ("jar".equals(protocol)) {
                JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                JarFile jarFile = jarURLConnection.getJarFile();
                // 遍歷Jar包
                Enumeration<JarEntry> entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry jarEntry = entries.nextElement();
                    String entityName = jarEntry.getName();
                    if (jarEntry.isDirectory() || !entityName.startsWith(dirPath)) {
                        continue;
                    }
                    if (entityName.endsWith(ext)) {
                        loadJarNative(jarEntry);
                    }
                }
            } else if ("file".equals(protocol)) {
                File file = new File(url.getPath());
                loadFileNative(file, ext);
            }

        }
    }

    private static void loadFileNative(File file, String ext) {
        if (null == file) {
            return;
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (null != files) {
                for (File f : files) {
                    loadFileNative(f, ext);
                }
            }
        }
        if (file.canRead() && file.getName().endsWith(ext)) {
            try {
                System.load(file.getPath());
                System.out.println("加載native文件 :" + file + "成功!!");
            } catch (UnsatisfiedLinkError e) {
                System.out.println("加載native文件 :" + file + "失敗!!請(qǐng)確認(rèn)操作系統(tǒng)是X86還是X64!!!");
            }
        }
    }

    /**
     * @throws IOException
     * @throws ClassNotFoundException
     * @Title: scanJ
     * @Description 掃描Jar包下所有class
     */
    /**
     * 創(chuàng)建動(dòng)態(tài)鏈接庫(kù)緩存文件左敌,然后加載資源文件
     *
     * @param jarEntry
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private static void loadJarNative(JarEntry jarEntry) throws IOException, ClassNotFoundException {

        File path = new File(".");
        //將所有動(dòng)態(tài)鏈接庫(kù)dll/so文件都放在一個(gè)臨時(shí)文件夾下,然后進(jìn)行加載
        //這是應(yīng)為項(xiàng)目為可執(zhí)行jar文件的時(shí)候不能很方便的掃描里面文件
        //此目錄放置在與項(xiàng)目同目錄下的natives文件夾下
        String rootOutputPath = path.getAbsoluteFile().getParent() + File.separator;
        String entityName = jarEntry.getName();
        String fileName = entityName.substring(entityName.lastIndexOf("/") + 1);
        System.out.println(entityName);
        System.out.println(fileName);
        File tempFile = new File(rootOutputPath + File.separator + entityName);
        // 如果緩存文件路徑不存在俐镐,則創(chuàng)建路徑
        if (!tempFile.getParentFile().exists()) {
            tempFile.getParentFile().mkdirs();
        }
        // 如果緩存文件存在矫限,則刪除
        if (tempFile.exists()) {
            tempFile.delete();
        }
        InputStream in = null;
        BufferedInputStream reader = null;
        FileOutputStream writer = null;
        try {
            //讀取文件形成輸入流
            in = NativeLoader.class.getResourceAsStream(entityName);
            if (in == null) {
                in = NativeLoader.class.getResourceAsStream("/" + entityName);
                if (null == in) {
                    return;
                }
            }
            NativeLoader.class.getResource(fileName);
            reader = new BufferedInputStream(in);
            writer = new FileOutputStream(tempFile);
            byte[] buffer = new byte[1024];

            while (reader.read(buffer) > 0) {
                writer.write(buffer);
                buffer = new byte[1024];
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (in != null) {
                in.close();
            }
            if (writer != null) {
                writer.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            System.out.println("path :" + tempFile.getPath());
            System.load(tempFile.getPath());
            System.out.println("加載native文件 :" + tempFile + "成功!!");
        } catch (UnsatisfiedLinkError e) {
            System.out.println("加載native文件 :" + tempFile + "失敗!!請(qǐng)確認(rèn)操作系統(tǒng)是X86還是X64!!!");
        }
    }
}
  • SpringBoot工程啟動(dòng)類新增初始化加載so的Bean(實(shí)現(xiàn)工程啟動(dòng)只加載一次so庫(kù))
@Bean
public void loadLib() {
    //根據(jù)操作系統(tǒng)判斷,如果是linux系統(tǒng)則加載c++方法庫(kù)
    String systemType = System.getProperty("os.name");
    String ext = (systemType.toLowerCase().indexOf("win") != -1) ? ".dll" : ".so";
    if (ext.equals(".so")) {
        try {
            NativeLoader.loader("native");
        } catch (Exception e) {
            System.out.println("加載so庫(kù)失敗");
        }
    }
    System.out.println("loaded");
}
  • 加載完成即可調(diào)用HelloJNI.java聲明的native方法

3.3 注意事項(xiàng)

android studio下編譯的jni庫(kù),在linux下springboot加載不成功叼风。必須在linux重新編譯一次動(dòng)態(tài)庫(kù)才能加載成功取董。

源碼工程
我的博客

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市无宿,隨后出現(xiàn)的幾起案子茵汰,更是在濱河造成了極大的恐慌,老刑警劉巖懈贺,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件经窖,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡梭灿,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門冰悠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)堡妒,“玉大人,你說(shuō)我怎么就攤上這事溉卓∑こ伲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵桑寨,是天一觀的道長(zhǎng)伏尼。 經(jīng)常有香客問(wèn)我,道長(zhǎng)尉尾,這世上最難降的妖魔是什么爆阶? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮沙咏,結(jié)果婚禮上辨图,老公的妹妹穿的比我還像新娘。我一直安慰自己肢藐,他們只是感情好故河,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著吆豹,像睡著了一般鱼的。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上痘煤,一...
    開(kāi)封第一講書(shū)人閱讀 52,457評(píng)論 1 311
  • 那天凑阶,我揣著相機(jī)與錄音,去河邊找鬼速勇。 笑死晌砾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的烦磁。 我是一名探鬼主播养匈,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼哼勇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了呕乎?” 一聲冷哼從身側(cè)響起积担,我...
    開(kāi)封第一講書(shū)人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎猬仁,沒(méi)想到半個(gè)月后帝璧,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡湿刽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年的烁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诈闺。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渴庆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出雅镊,到底是詐尸還是另有隱情襟雷,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布仁烹,位于F島的核電站耸弄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏卓缰。R本人自食惡果不足惜计呈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望僚饭。 院中可真熱鬧震叮,春花似錦、人聲如沸鳍鸵。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)偿乖。三九已至击罪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贪薪,已是汗流浹背媳禁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留画切,地道東北人竣稽。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親毫别。 傳聞我的和親對(duì)象是個(gè)殘疾皇子娃弓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

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