可执行Jar包调用动态链接库(DLL/SO)
踩过了很多的坑,查了很多资料,在此记录一下,以SpringBoot项目为基础。
Maven加入JNA依赖
<!-- JNA start -->
<dependency>
<groupId>com.sun.jna</groupId>
<artifactId>jna</artifactId>
</dependency>
<!-- JNA end -->
动态链接库放在classpath下的natives文件夹下
主入口中的代码
@ServletComponentScan
@SpringBootApplication
@ComponentScan("com.yunzhitx.sdy")
@MapperScan(basePackages = "com.yunzhitx.sdy.core.**.infra")
public class TaskApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
} public static void main(String[] args) {
//加载动态链接库,注意和SpringBoot的启动
String systemType = System.getProperty("os.name");
if (systemType.toLowerCase().indexOf("win") != -1)
loadNative("dhnetsdk");
else
loadNative("libdhnetsdk");
SpringApplication.run(TaskApplication.class);
} private synchronized static void loadNative(String nativeName) { String systemType = System.getProperty("os.name");
String fileExt = (systemType.toLowerCase().indexOf("win") != -1) ? ".dll" : ".so"; // String sysUserTempDir = System.getProperty("java.io.tmpdir");
/*String javaLibraryPath = System.getProperty("java.library.path");
String sysUserTempDir = "" ;
if(systemType.toLowerCase().indexOf("win") != -1) {
String[] dirs = javaLibraryPath.split(";");//分号
sysUserTempDir = dirs[0];
}else {
String[] dirs = javaLibraryPath.split(":"); //冒号
sysUserTempDir = dirs[0];
}*/ 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文件夹下。
在通过编写好的接口类进行实例化操作:
NetSDKLib monitorNetSdk = (NetSDKLib) Native.loadLibrary("dhnetsdk", NetSDKLib.class);
Linux系统下,需要将放置dll/so文件的文件夹加入到环境变量中,否则依然会提示找不到文件的错误:
export LD_LIBRARY_PATH=/home/sdy_task/natives
但是这种方式是临时性的,一旦重启就将失效,所有建议写入配置文件中:
修改/etc/profile,添加如下代码
LD_LIBRARY_PATH=/home/sdy_task/natives
export LD_LIBRARY_PATH
升级版
package org.yoki.edu.image.utils; import java.io.*;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile; /**
* @author Sky$
* @Description: TODO
* @date 2018/2/8$ 17:55$
*/
public class NativeLoader { /**
* 加载项目下的native文件,DLL或SO
*
* @param dirPath 需要扫描的文件路径,项目下的相对路径
* @throws IOException
* @throws ClassNotFoundException
*/
public synchronized static void loader(String dirPath) throws IOException, ClassNotFoundException {
Enumeration<URL> dir = Thread.currentThread().getContextClassLoader().getResources(dirPath);
// 获取操作系统类型
String systemType = System.getProperty("os.name");
String systemArch = System.getProperty("os.arch");
// 获取动态链接库后缀名
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 + "失败!!请确认操作系统是X86还是X64!!!");
}
}
} /**
* @throws IOException
* @throws ClassNotFoundException
* @Title: scanJ
* @Description 扫描Jar包下所有class
*/
/**
* 创建动态链接库缓存文件,然后加载资源文件
*
* @param jarEntry
* @throws IOException
* @throws ClassNotFoundException
*/
private static void loadJarNative(JarEntry jarEntry) throws IOException, ClassNotFoundException { File path = new File(".");
//将所有动态链接库dll/so文件都放在一个临时文件夹下,然后进行加载
//这是应为项目为可执行jar文件的时候不能很方便的扫描里面文件
//此目录放置在与项目同目录下的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);
// 如果缓存文件路径不存在,则创建路径
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.load(tempFile.getPath());
System.out.println("加载native文件 :" + tempFile + "成功!!");
} catch (UnsatisfiedLinkError e) {
System.out.println("加载native文件 :" + tempFile + "失败!!请确认操作系统是X86还是X64!!!");
} } }
可执行Jar包调用动态链接库(DLL/SO)的更多相关文章
- 【java-console】如何双击运行可执行jar包及遇到依赖dll报错问题的解决办法
如何配置双击运行可执行jar包的步骤,请移步到 这里 查看具体的操作,此处不再介绍. 本文主要解决如何处理依赖dll报错的问题解决办法. 我有一个jar包可执行文件运行需要依赖第三方的dll文 ...
- Jar包转成Dll的方式(带嵌套的jar也能做) (转)
研究很好几天,终于成功了.因为写了一个Java的项目,现在要求要改写成C#版本的.但是其中用到了svnkit,svnkit是java平台的.改写成C#的话,要使用SharpSVN,但是SharpSVN ...
- Jar包转成Dll的方式(带嵌套的jar也能做)
研究很好几天,终于成功了.因为写了一个Java的项目,现在要求要改写成C#版本的.但是其中用到了svnkit,svnkit是java平台的.改写成C#的话,要使用SharpSVN,但是SharpSVN ...
- SpringBoot+Maven多模块项目(创建、依赖、打包可执行jar包部署测试)完整流程
一,创建Maven多模块项目先建立外层父工程 File →new →project 选择Spring Initializr Next下一步到以下页面 工程结构如下 ...
- JAVA生成(可执行)Jar包的全面详解说明 [打包][SpringBoot][Eclipse][IDEA][Maven][Gradle][分离][可执行]
辛苦所得,转载还请注明: https://www.cnblogs.com/applerosa/p/9739007.html 得空整理了关于java 开发中,所有打包方式的 一个操作方法, 有基于ID ...
- eclipse导出maven工程的可执行jar包
一.eclipse导出maven工程的可执行jar包 建立两个maven工程 ZKServer 和ZKClient 注意:用maven进行开发管理的话,默认的打出来的jar包是不能运行的,需要在pom ...
- IDEA 直接点击运行执行正常,命令行下面执行Jar包出现部分乱码的情况。
解决方案如上: 有个Springboot项目为了测试方便,模型类用中文作为字段属性,封装成Odata格式,在通过Springboot发布并打成jar包. 通过命令行启动jar包里面的Springweb ...
- Spring Boot可执行Jar包运行原理
目录 1. 打可执行Jar包 2. 可执行Jar包内部结构 3. JarLauncher 4. 简单总结 5. 远程调试 Spring Boot有一个很方便的功能就是可以将应用打成可执行的Jar.那么 ...
- Unity通过Jar包调用Android
Unity通过Jar包调用Android 我们会需要面对下面几个问题,我们一个一个来解决: 怎样在Android Studio中打Jar包 怎样打一个Unity使用的Jar包 怎样在Unity工程中使 ...
随机推荐
- xCode控制台的使用-应用闪退原因跟踪
xCode控制台的使用-应用闪退原因跟踪 今天遇到这个一个问题,使用xCode version = 5.0 编译一个程序,刷到IOS7设备后,应用运行正常,但是刷新到IOS<7,打开饮用后就会出 ...
- gridview行链接
原文发布时间为:2009-04-21 -- 来源于本人的百度文章 [由搬家工具导入] 点击行,链接!! 可这样,在GridView的RowDataBound输入代码,假如id在第0列,且不是摸板列: ...
- duilib入门简明教程 -- 响应按钮事件(4) (转)
原文转自 http://www.cnblogs.com/Alberl/p/3343610.html 上一个Hello World的教程里有一句代码是这样的:CControlUI *pWnd = ...
- c#反射,委托,事件
1.反射,通过类名来实例化类 //用构造函数动态生成对象: Type t = typeof(NewClassw); Type[] pt = ]; pt[] = typeof(string); pt[] ...
- CodeChef February Challenge 2018 Points Inside A Polygon (鸽笼原理)
题目链接 Points Inside A Polygon 题意 给定一个$n$个点的凸多边形,求出$[ \frac{n}{10}]\ $个凸多边形内的整点. 把$n$个点分成$4$类: 横坐标奇, ...
- 牛客练习赛10 E题 数列查找 (分块思想 + 莫队算法)
题目链接 数列查找 考虑分块然后跑莫队, 设$c[i]$为$i$在当前维护的区间内出现的次数, $g[i]$为在当前维护的区间内有多少个数出现次数为$i$, $bg[i]$把出现次数分块,$bg[i ...
- Codeforces Gym 101471D Money for Nothing(2017 ACM-ICPC World Finals D题,决策单调性)
题目链接 2017 ACM-ICPC World Finals Problem D (这题细节真的很多) 把所有的(pi,di)按横坐标升序排序. 对于某个点,若存在一个点在他左下角,那么这个点就是 ...
- weblogic内存快速配置
# IF USER_MEM_ARGS the environment variable is set, use it to override ALL MEM_ARGS values USER_MEM_ ...
- ETL之Kettle
Kettle是一款国外开源的ETL工具,纯java编写,可以在Window.Linux.Unix上运行. 说白了就是,很有必要去理解一般ETL工具必备的特性和功能,这样才更好的掌握Kettle的使用. ...
- Scala之Future
一.简介 Future提供了一套高效便捷的非阻塞并行操作管理方案.其基本思想很简单,所谓Future,指的是一类占位符对象,用于指代某些尚未完成的计算的结果.一般来说,由Future指代的计算都是并行 ...