可执行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工程中使 ...
随机推荐
- ef code first transform,add ef power tools add-in,add tangible t4 editor for enhancement.
use ef power tools, as to .edmx file,right click at view, choose generate database from model, then ...
- LeetCode OJ-- Jump Game II **
https://oj.leetcode.com/problems/jump-game-ii/ 给一个数列,每次可以跳相应位置上的步数,问跳到最后位置至少用几步. 动态规划: j[pos]表示从0到po ...
- 16Aspx.com-将15位身份证转换成18位
//********************************************************************************* //将15位身份证转换成18位时 ...
- Codeforces Gym101063 J.The Keys (2016 USP-ICMC)
J.The Keys Out of all science labs constructed by the GEMA mission on Mars, the DSL - Dangerous Spec ...
- Codeforces Gym100971 L.Chess Match (IX Samara Regional Intercollegiate Programming Contest Russia, Samara, March 13)
这个题就是两个队,看最多能赢的个数,然后比较一下,看两个队是都能赢彼此,还是只有一个队赢的可能性最大.表达能力不好,意思差不多... 和田忌赛马有点像,emnnn,嗯. 代码: 1 #include& ...
- Codeforces 899 B.Months and Years
B. Months and Years time limit per test 1 second memory limit per test 256 megabytes input standar ...
- 判断图连通的三种方法——dfs,bfs,并查集
Description 如果无向图G每对顶点v和w都有从v到w的路径,那么称无向图G是连通的.现在给定一张无向图,判断它是否是连通的. Input 第一行有2个整数n和m(0 < n,m < ...
- 深入V8引擎-Time核心方法之win篇(1)
上一篇的源码看得十分无趣,官方文档跟黑心棉一样渣. 这一篇讲讲windows操作系统上的时间戳实现,由于类的声明,方法解释上一篇都贴过了,所以这次直接上对应版本的代码. windows与mac很不一样 ...
- luogu P1616 疯狂的采药
题目背景 此题为NOIP2005普及组第三题的疯狂版. 此题为纪念LiYuxiang而生. 题目描述 LiYuxiang是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师.为此,他想拜附近最有威望的 ...
- 怎样去主动拿一个锁并占有?synchronized关键字即可
怎样主动去拿一个?synchronized关键字即可 怎样去释放一个锁呢?要求锁对象主动释放,打乱占有当前锁的线程即可