Java通过JNI调用C++动态链接库dll,并打在jar包内 ——JNA-JNI(一)
Java通过JNI调用C++动态链接库,并打在jar包内JNI介绍创建JAVA项目C++对应的JAVA对象类windows环境下制作C++动态链接库JAVA程序调用打jar包windows平台下遇到的问题
Java通过JNI调用C++动态链接库dll,并打在jar包内——JNA-JNI(一)
系列文章:
Java通过JNI调用C++动态链接库dll,并打在jar包内 ——JNA-JNI(一)
Java使用JNA调用C++动态链接库——JNA-JNI(二)
Mac M1 Xcode创建动态链接库dylib(c++)——JNA-JNI(三)
JNA调用dll(c++)附带解析xml——JNA-JNI(四)
JNA参数类型转换(含接收、发送结构体)——JNA-JNI(五)
目录
JNI介绍
JNI(Java Native Interface):允许Java代码和其他语言(尤其C/C++)写的代码进行交互,只要遵守调用约定即可。首先看下JNI调用C/C++的过程,注意写程序时自下而上,调用时自上而下。

xx.dll就像中介一样,Java通过调用这个中介Dll中的自定义方法,间接调用真正的第三方Dll
项目以windows为例,后缀为dll,若为linux,后缀修改为so即可。
创建JAVA项目
这里我用的是spring(为了扩展),使用纯java也是可以的。项目结构如下:

C++对应的JAVA对象类
- 类的内容
public class Demo {
public native void sayHello(int x,int y);
}
- 生成.class文件
javac Demo.java
- 生成.h文件
错误: 找不到 ‘Demo’ 的类文件。
切换到目录src\main\java\这个目录下,重新执行
javah com.example.demo.Demo
执行成功之后会在com包同级目录下出现一个xx.h的文件。

windows环境下制作C++动态链接库
使用VS2017来演示
-
新建空项目
-
将.h文件复制过来并添加
无法打开包括文件:“xxx.h”: No such file or directory.错误。
- 出现此问题的原因:把头文件复制,直接选择项目粘贴进来,虽然解决方案资源管理器里显示此头文件,但是编译就出现上面的错误,找不到头文件。
- 打开项目目录,发现里面不存在刚才复制的头文件,这是微软的BUG,需要打开项目目录把文件复制过来。
- 所以引用头文件的正确顺序是,先把头文件复制到项目目录里,然后选择打开VS,选择项目右键->添加->现有项,选择复制到项目里的头文件。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_demo_Demo */
#ifndef _Included_com_example_demo_Demo
#define _Included_com_example_demo_Demo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_demo_Demo
* Method: sayHello
* Signature: (II)V
*/
JNIEXPORT void JNICALL Java_com_example_demo_Demo_sayHello
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
- 编写接口实现cpp文件
#include <iostream>
#include "com_example_demo_Demo.h"
using namespace std;
JNIEXPORT void JNICALL Java_com_example_demo_Demo_sayHello(JNIEnv *env, jobject obj, jint x, jint y)
{
cout << "x:" << x << "; y:" << y << "x+y=" << x+y << endl;
}
-
修改项目属性
直接生成方案会出新以下问题,其实是初始化项目后,没有修改为dll类型。
在配置中修改后即可。
-
配置包含目录
添加JDK/include路径,因为jni.h在这个路径下,或者直接将jni.h也复制到项目的头文件里。 -
配置dll对应x64还是x86
需要注意的是,生成的dll动态链接库,需要和系统位数对应,VS默认是32位的,如果服务器是64位的,需要手动修改平台再生成。
-
生成dll链接库
点击生成解决方案,在项目文件夹下会生成.dll文件
-
项目结构
JAVA程序调用
这里直接在main函数里调用了
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
System.load("C:\\xx\\Project6.dll");
Demo demo = new Demo();
demo.sayHello(2,3);
}
}

打jar包
-
将dll文件放在java项目的
src/main/resources/native/
目录下 -
添加文件解析函数
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class);
//加载动态链接库,注意和SpringBoot的启动
String systemType = System.getProperty("os.name");
if (systemType.toLowerCase().indexOf("win") != -1)
loadNative("Project6");
else
loadNative("Project6");
Demo demo = new Demo();
demo.sayHello(2, 3);
}
private synchronized static void loadNative(String nativeName) {
String systemType = System.getProperty("os.name");
String fileExt = (systemType.toLowerCase().indexOf("win") != -1) ? ".dll" : ".so";
File path = new File(".");
//将所有动态链接库dll/so文件都放在一个临时文件夹下,然后进行加载
//这是应为项目为可执行jar文件的时候不能很方便的扫描里面文件
//此目录放置在与项目同目录下的natives文件夹下
String sysUserTempDir = path.getAbsoluteFile().getParent() + File.separator + "natives";
System.out.println("------>>native lib临时存放目录 : " + sysUserTempDir);
String fileName = nativeName + fileExt;
InputStream in = null;
BufferedInputStream reader = null;
FileOutputStream writer = null;
File tempFile = new File(sysUserTempDir + File.separator + fileName);
if(!tempFile.getParentFile().exists())
tempFile.getParentFile().mkdirs() ;
if (tempFile.exists()) {
tempFile.delete();
}
try {
//读取文件形成输入流
in = TaskApplication.class.getResourceAsStream("/native/" + fileName);
if (in == null)
in = TaskApplication.class.getResourceAsStream("native/" + fileName);
TaskApplication.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();
} finally {
try {
if (in != null)
in.close();
if (writer != null)
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.load(tempFile.getPath());
System.out.println("------>> 加载native文件 :" + tempFile + "成功!!");
}
}
-
主入口函数的代码主要是进行加载操作,也可以在需要使用到的地方在进行加载。
-
加载的时候进行了如下操作:
- 1、将所有动态链接库dll/so文件都放在一个临时文件夹下。
- 2、读取临时文件IO流进行加载。
-
因为项目为可执行jar文件的时候不能很方便的扫描里面文件,此目录代码放置在与项目同目录下的natives文件夹下。
-
运行jar包
windows平台下遇到的问题
-
LNK1104:无法打开“kernel32.lib”
原文链接:https://blog.csdn.net/Beyond111223/article/details/82913628
找到“kernel32.lib”的路径,一般如下:
打开:项目---->属性---->配置属性---->VC++ 目录---->库目录
添加路径
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib
-
LNK1158:无法运行“rc.exe”
-
项目属性-常规-平台工具集里,将Visual Studio 2013 (v120)替换为Visual Studio 2013 - Windows XP (v120_xp)。
-
打开系统环境变量列表(我的电脑—>属性—>高级—>环境变量),在系统变量栏里看Path,添加:
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin\;
其他可能需要的目录,来自https://www.cnblogs.com/Jimnny/p/3574368.html
C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\; C:\Program Files (x86)\Windows Kits\8.0\Windows Performance Toolkit\; C:\Program Files\Microsoft SQL Server\110\Tools\Binn\; C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\; C:\Users\(用户名)\AppData\Local\Microsoft\MSBuild\v4.0\;
-
-
Can’t load IA 32-bit .dll on a AMD 64-bit platform
原因:xx.dll是32位的不支持64位的平台(jdk环境)
解决方法:VS使用x64重新编译(VS默认是32位)
-
step 1
-
step 2
-
step 3 把ARM改为x64,其他不动
-
step 4
-
step 5 运行
-
-
getresourceasstream方法获取值为null
解决方法:重新配置一下
pom.xml
的build
,刷新maven库<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
-
离线环境打包失败
方法:使用打包命令
mvn clean install -Dmaven.test.skip=true
更多推荐
所有评论(0)