spring boot jar包启动,JNI相对路径加载dll方案一
前言在java调用dll的项目中,之间的部署的方式,是需要手动提前将所需的dll,替换放在jdkbin文件夹或者C:\Windows\System32文件夹的下,后续开发的过程中,dll文件需要不断更新,一方面dll版本维护成为比较麻烦的事情,还有部署方式略显繁琐,经过一段的摸索后,在项目jar包启动的时候实现dll自动部署的方案。原理JNI的加载方式分为两种一种是动态加载就是在JDKbin文件或
前言
在java调用dll的项目中,之间的部署的方式,是需要手动提前将所需的dll,替换放在jdkbin文件夹或者C:\Windows\System32文件夹的下,后续开发的过程中,dll文件需要不断更新,一方面dll版本维护成为比较麻烦的事情,还有部署方式略显繁琐,经过一段的摸索后,在项目jar包启动的时候实现dll自动部署的方案。
原理
JNI的加载方式分为两种一种是动态加载就是在JDKbin文件或者\System32文件夹的下,寻找dll加载,一种是利用绝对路径去加载,之前利用绝对路径去加载的时候,会遇到一个问题,因为dll在jar包里面,获取的绝对路径中间都会包含“XXX.jar!”,导致dll加载失败,想到的思路是,在项目启动的时候,自动将dll输出到jar所在的同级目录下,然后再根据新输出dll的绝对路径去加载即可。
项目中dll的位置

代码
核心代码示例
@PostConstruct
private void initDll() throws IOException {
String dllPath = this.getClass().getResource("/dll/").getPath() + "DwgOperInterface.dll";
if (dllPath.indexOf(".jar") > 0) {
dllPath = dllPath.substring(0, dllPath.lastIndexOf(".jar"));
dllPath = dllPath.substring(6, dllPath.lastIndexOf("/"));
dllPath = File.separator + dllPath + "/DLL";
copyDir(dllPath);
System.out.println("dll加载路径:" + dllPath);
System.load(dllPath + "/DwgOperInterface.dll");
} else {
System.load(dllPath);
}
}
/**
* 复制文件夹下所有文件
*
* @author tarzan LIU
* @date 2020/10/22
*/
private void copyDir(String javaUrl) throws IOException {
URL url = JNIMapUtil.class.getClassLoader().getResource("dll/");
String jarPath = url.toString().substring(0, url.toString().indexOf("!/") + 2);
URL jarURL = new URL(jarPath);
JarURLConnection jarCon = (JarURLConnection) jarURL.openConnection();
JarFile jarFile = jarCon.getJarFile();
Enumeration<JarEntry> jarEntrys = jarFile.entries();
while (jarEntrys.hasMoreElements()) {
JarEntry entry = jarEntrys.nextElement();
String name = entry.getName();
if (name.contains("dll/") && !entry.isDirectory()) {
String[] strs = name.split("/");
copyFile(strs[strs.length - 1], javaUrl);
log.info("file name is :" + strs[strs.length - 1] + " finish copy!!");
}
}
}
/**
* 复制一份dll放到系统临时文件夹中,处理jar包,dll无法读取问题(代替loadLibrary)
*
* @author tarzan LIU
* @date 2020/10/16
*/
private synchronized static void copyFile(String libName, String javaUrl) throws IOException {
InputStream in = null;
FileOutputStream writer = null;
File make = new File(javaUrl);
if (!make.exists() && !make.isDirectory()) {
make.mkdirs();
}
File extractedLibFile = new File(javaUrl + File.separator + libName);
try {
String dllUrlInJar = "/dll/";
in = JNIMapUtil.class.getResourceAsStream(dllUrlInJar + libName);
if (in == null)
in = JNIMapUtil.class.getResourceAsStream(libName);
JNIMapUtil.class.getResource(libName);
BufferedInputStream reader = new BufferedInputStream(in);
writer = new FileOutputStream(extractedLibFile);
byte[] buffer = new byte[1024];
while (reader.read(buffer) > 0) {
writer.write(buffer);
buffer = new byte[1024];
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null)
in.close();
if (writer != null)
writer.close();
}
}
相关知识
Java Native Interface(JNI)是Java平台提供的一种机制,用于在Java应用程序中与本地代码进行交互。通过JNI,Java程序可以调用本地代码(通常是C或C++编写的),并且本地代码也可以调用Java代码。在接下来,我将为您详细解释JNI的主要概念、用途和基本用法。
JNI的主要概念:
- Native方法:Native方法是指在Java类中声明但不实现的方法,它使用关键字
native修饰。这些方法的实现由本地代码提供,并通过JNI接口与Java代码进行交互。 - JNI接口:JNI提供了一组函数和数据结构,用于在Java代码和本地代码之间进行通信。开发者可以使用JNI接口访问Java虚拟机(JVM)内部的数据和功能。
- JNI环境:JNI环境是一个指向JNI接口函数表的指针,它提供了访问Java运行时环境的能力,如获取对象引用、调用Java方法等。
- 本地库:本地库是包含本地代码的动态链接库文件,通常使用C/C++编写。Java程序需要加载本地库才能调用其中的本地方法。
JNI的主要用途:
- 提供底层功能:JNI允许Java程序调用本地代码,以实现一些底层功能,如访问操作系统API、硬件设备驱动程序等。
- 性能优化:JNI允许将性能关键的任务委托给本地代码,以提高执行效率。本地代码通常比Java代码更接近底层硬件和操作系统,可以通过优化算法或使用与平台相关的特性来实现更高的性能。
- 跨平台支持:JNI提供了一种跨平台的机制,使得Java程序可以在不同的操作系统上调用适当的本地库,从而实现平台无关性。
基本使用方法:
-
编写本地方法的实现:使用C/C++编写包含本地方法的实现代码。这些方法需要遵循JNI规范,并使用合适的数据类型和函数进行交互。
-
生成本地库文件:使用本地编译器(如gcc或Visual Studio)将本地方法的实现代码编译为本地库文件(例如动态链接库.so或.dll文件)。
-
在Java类中声明本地方法:在Java类中使用
native关键字声明与本地方法对应的方法。方法签名必须与本地方法的实现一致。 -
加载本地库:在Java代码中使用
System.loadLibrary()方法加载本地库。该方法接受库文件名作为参数,会搜索并加载具有相应名称的本地库。 -
调用本地方法:通过普通Java代码调用声明的本地方法。在运行时,JVM将通过JNI接口将方法调用传递给本地库,执行对应的本地代码。
简单的示例:
下面是一个简单的示例,展示了如何使用JNI调用本地方法:
1.编写本地方法的实现(C代码):
#include <jni.h>
JNIEXPORT jstring JNICALL Java_com_example_MyClass_getHello(JNIEnv *env, jobject obj) {
return (*env)->NewStringUTF(env, "Hello from native code!");
}
2.生成本地库文件:
gcc -shared -fpic -o libmylib.so MyLib.c
3.在Java类中声明本地方法:
public class MyClass {
public native String getHello();
}
4.加载本地库:
System.loadLibrary("mylib");
5.调用本地方法:
MyClass myObj = new MyClass();
String result = myObj.getHello();
System.out.println(result);
总结: JNI是Java平台提供的一种机制,用于在Java应用程序中与本地代码进行交互。通过JNI,我们可以调用本地代码并让本地代码调用Java代码,从而实现底层功能、性能优化和跨平台支持。使用JNI涉及编写本地方法实现、生成本地库文件、声明本地方法并加载本地库。JNI是一项强大的技术,可以将Java与本地代码紧密结合,扩展Java的能力和应用场景。请注意,以上解释只是对JNI的基本介绍,如果您有特定的问题或需求,请进一步详细了解JNI的官方文档和资料。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)