spring boot jar包启动JNI相对路径加载dll方案二

Posted 洛阳泰山

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring boot jar包启动JNI相对路径加载dll方案二相关的知识,希望对你有一定的参考价值。

 前言

在java调用dll的项目中,之间的部署的方式,是需要手动提前将所需的dll,替换放在jdkbin文件夹或者C:\\Windows\\System32文件夹的下,后续开发的过程中,dll文件需要不断更新,一方面dll版本维护成为比较麻烦的事情,还有部署方式略显繁琐,经过一段的摸索后,在项目jar包启动的时候实现dll自动部署的方案。

原理

JNI的加载方式分为两种一种是动态加载就是在JDKbin文件或者\\System32文件夹的下,寻找dll加载,一种是利用绝对路径去加载,之前利用绝对路径去加载的时候,会遇到一个问题,因为dll在jar包里面,获取的绝对路径中间都会包含“XXX.jar!”,导致dll加载失败,想到的思路是,在项目启动的时候,自动将dll输出到jar所在的同级目录下,然后再根据新输出dll的绝对路径去加载即可。

项目中dll的位置,之前方案一《spring boot jar包启动,JNI相对路径加载dll方案一》,dll实在resources下,dll文件打入到了jar包中,然后启动时候把dll文件复制到了jar包的同级目录,进行加载的。此方案,因为设计到多套dll文件,且dll文件过大,dll的更新的频率很小,每次打包部署太浪费时间,所以,不打包到jar里,部署时候需要手动放在jar的同级目录。

 

核心代码示例

 创建 JniUtil工具类只负责加载和卸载dll。可以实现多了dll入口文件的灵活加载和卸载,代码如下,创建JniUtil 类,复制粘贴即可(   @PostConstruct 注解是项目启动时候就执行方法,代码中启动加载了dll_1,根据自己的需求设置,启动加载)


import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Vector;

@Component
@Slf4j
public class JniUtil {


    @PostConstruct
    public static void loadDll_1() {
        loadDLL("dll_1", "auoLoad.dll");
    }

    public static void loadDll_2() {
        loadDLL("dll_2", "projectForJava.dll");
    }

    public static void unloadDll_1() { unloadDLL("auoLoad.dll"); }

    public static void unloadDll_2() {
        unloadDLL("projectForJava.dll");
    }


    private synchronized static void loadDLL(String folderName, String libName) {
        String classPath = new JniUtil().getClass().getResource("/").getPath();
        if (classPath.indexOf(".jar") > 0) {
            classPath = classPath.substring(0, classPath.lastIndexOf(".jar"));
            classPath = classPath.substring(6, classPath.lastIndexOf("/"));
            String dllPath = File.separator + classPath + "/" + folderName;
            log.info("========dll加载路径:" + dllPath + "========");
            System.load(dllPath + "/" + libName);
        } else {
            String projectPath = classPath.replace("target/classes/", "");
            String dllPath = projectPath + folderName + "/" + libName;
            log.info("========dll加载路径:" + dllPath + "========");
        }
        log.info("========dll加载成功========");
    }


    public synchronized static void unloadDLL(String libName) {
        try {
            ClassLoader classLoader = JniUtil.class.getClassLoader();
            Field field = ClassLoader.class.getDeclaredField("nativeLibraries");
            field.setAccessible(true);
            Vector<Object> libs = (Vector<Object>) field.get(classLoader);
            Iterator it = libs.iterator();
            while (it.hasNext()) {
                Object object = it.next();
                Field[] fs = object.getClass().getDeclaredFields();
                for (int k = 0; k < fs.length; k++) {
                    if (fs[k].getName().equals("name")) {
                        fs[k].setAccessible(true);
                        String dllPath = fs[k].get(object).toString();
                        if (dllPath.endsWith(libName)) {
                            Method finalize = object.getClass()
                                    .getDeclaredMethod("finalize");
                            finalize.setAccessible(true);
                            finalize.invoke(object);
                        }
                    }
                }
            }
            log.info("========dll卸载成功========");
        } catch (Throwable th) {
            log.error("========dll卸载失败========");
            th.printStackTrace();
        }
    }


}

jni接口代码如下

package com.hxtx.stripmine.jni;


import org.springframework.stereotype.Component;

/**
 *
 *  JNI调用dll方法
 *
 * @since JDK1.8
 * @author tarzan Liu
 * @date 2021年04月01日 14:37:43
 */
@Component
public class GraphicJNIService {



    /**
     * 方法描述: 解析dem材质边界
     *
     * @param fileUrl (dwg文件地址)
     * @Return {@link String}
     * @author tarzan Liu
     * @date 2021年04月01日 14:38:05
     */
    public native  String  parseLayer(String fileUrl);


    /**
     * 方法描述: dwg文件编译1
     *
     * @param tempFolder 中间文件文件夹绝对路径
     * @param dwgFolder 所需材料文件夹绝对路径
     * @param materialInfo 当前启用的地形材质配置信息
     * @param netWorkJsonPath 生成的路网json文件绝对路径
     * @param elevationJsonPath 生成的高程json文件绝对路径
     * @param x: 438000, y: 4964000 北京54参照点
     * @param flag 编译类型:1两者都编译  2只编译路网(只生成路网json,不再调用韩总dll)
     * @Return {@link String}
     * @author tarzan Liu
     * @date 2021年04月01日 14:39:43
     */
    public native  String compileDWG(String tempFolder,String dwgFolder,String materialInfo,String netWorkJsonPath,String elevationJsonPath,double x,double y,int flag);

    /**
     * 方法描述: dwg文件编译2
     *
     * @param tempFolder 中间文件文件夹绝对路径
     * @param resultFolder 成果文件夹绝对路径
     * @param x,y  compileDWG()方法返回的x,y
     * @param lng,lat 经纬度参照点
     * @Return {@link String}
     * @author tarzan Liu
     * @date 2021年04月01日 14:39:43
     */
    public native  String tranDataToB3dm(String tempFolder, String resultFolder, double lng, double lat, double x, double y, double heigh);

}

项目Service类中如何调用,示例代码如下

 @Resource
 private GraphicJNIService graphicJNIService;


 @Transactional(rollbackFor = Throwable.class)
    public void compileDemDwg(){
            JniUtil.loadDll_2();
            //省略部分代码
graphicJNIService.tranDataToB3dm(tempFolder,demJsonPath,89.21598146531036,44.808601928195955,x,y,0);

            JniUtil.unloadDll_2();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

以上是关于spring boot jar包启动JNI相对路径加载dll方案二的主要内容,如果未能解决你的问题,请参考以下文章

spring boot 打jar包,获取resource路径下的文件

springboot jar启动 读取jar包中相对路径文件报错

Spring Boot:通过jar包启动

Spring boot项目以jar包形式启动中文乱码

linux下部署Spring boot jar包

Spring Boot 项目访问依赖 jar 包内部的资源文件的路径问题详解