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ù)才能加載成功取董。