zoukankan      html  css  js  c++  java
  • 可执行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!!!");
            }
    
        }
    
    }
  • 相关阅读:
    单例模式
    collections额外数据类型
    logging的简单使用
    杂记
    字符编码
    面向对象编程简介
    logging模块
    re与subprocess模块
    oepnpyxl模块 与excle交互
    json序列化模块
  • 原文地址:https://www.cnblogs.com/FlyingPuPu/p/7598098.html
Copyright © 2011-2022 走看看