Java调用C++动态库(DLL)的完整实践指南

 更新时间:2025年08月27日 09:04:04   作者:颇有几分姿色  
这篇文章主要为大家详细介绍如何通过 JNI(Java Native Interface)在 Java 中调用一个用 C++ 编写的分割算法库,有需要的小伙伴可以了解一下

Java 通过 JNI 调用 C++ 动态库的完整流程

这里主要介绍如何通过 JNI(Java Native Interface)在 Java 中调用一个用 C++ 编写的分割算法库。不涉及图像和 java 实体处理。环境如下:

  • 系统:Windows 10
  • JDK:1.8
  • IDE:Visual Studio 2022
  • Java 构建工具:Maven
  • 目标平台:x64

一、整体目标

Java 端通过 JNI 调用算法提供的动态库,实现图像分割与信息提取的功能。为了方便,我把所有的依赖库都放在了 jdk 的 bin 目录,这样我在调用的时候只需要导入我的自己生成的 jni 库就可以,如果需要频繁切换 jdk,那需要自己指定目录,处理好动态库的依赖关系即可。

二、准备工作

1. C++ 头文件(API 定义)

这是算法的头文件,jni 层需要根据他的方法调用。建议和 java 的方法保持一致,不保持一致也行,自己在 jni 层处理好就可以。

// passportSegC.h
DLL_API const char* passport_seg_get_version();
DLL_API int passport_seg_init(const int equip_type, const int passport_type, const char* model_path, const char* config_path);
DLL_API const char* passport_seg_run(const char* json_str_c, const char* save_root_c, int* code_passport_offset_det);
DLL_API void passport_seg_release_per_call(const char* json_ctr);
DLL_API void passport_seg_release();

2. Java 接口类定义

package com.emp.empxmrz.util;

/***
 * @title
 * @author 
 * @date 2025/8/7 10:58
 **/
public class PassportSeg {
    static {
        System.loadLibrary("passportSeg"); 
    }

    // 获取版本号
    public static native String passportSegGetVersion();

    // 算法初始化
    public static native int passportSegInit(int equipType, int passportType, String modelPath, String configPath);

    // 调用算法
    public static native String passportSegRun(String jsonStr, String saveRoot, int[] codePassportOffsetDet);

    // 每次调用算法后释放内存
    public static native void passportSegReleasePerCall(String jsonCtr);

    // 程序终止时释放AI模型内存
    public static native void passportSegRelease();
}

三、生成 JNI 头文件

javah -classpath target/classes -d src/main/jni com.emp.empxmrz.util.PassportSeg

这会生成 com_emp_empxmrz_util_PassportSeg.h,它定义了 JNI 接口供 C++ 实现。生成之后不能随便移动类的位置或修改包名、类名等,如果必须调整的话,需要重新生成。

生成的头文件大概如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_emp_empxmrz_util_PassportSeg */

#ifndef _Included_com_emp_empxmrz_util_PassportSeg
#define _Included_com_emp_empxmrz_util_PassportSeg
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_emp_empxmrz_util_PassportSeg
 * Method:    passportSegGetVersion
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegGetVersion
  (JNIEnv *, jclass);

/*
 * Class:     com_emp_empxmrz_util_PassportSeg
 * Method:    passportSegInit
 * Signature: (IILjava/lang/String;Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegInit
  (JNIEnv *, jclass, jint, jint, jstring, jstring);

/*
 * Class:     com_emp_empxmrz_util_PassportSeg
 * Method:    passportSegRun
 * Signature: (Ljava/lang/String;Ljava/lang/String;[I)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegRun
  (JNIEnv *, jclass, jstring, jstring, jintArray);

/*
 * Class:     com_emp_empxmrz_util_PassportSeg
 * Method:    passportSegReleasePerCall
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegReleasePerCall
  (JNIEnv *, jclass, jstring);

/*
 * Class:     com_emp_empxmrz_util_PassportSeg
 * Method:    passportSegRelease
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegRelease
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

四、JNI 实现(C++)

这是 jni 的头文件实现,因为我是要调用多个算法,所以使用 vs 创建了一个解决方案,里面创建了多个项目,每个项目都是一种算法的 jni 层,根据自己的实际情况操作就可以,实现类中需要包含算法头文件和 jni 头文件。项目结果大致如下:

cpp 完整代码如下:

#include <jni.h>
#include <iostream>
#include <stdexcept>
#include "com_emp_empxmrz_util_PassportSeg.h"
#include "passportSegC.h"

void native_log(const std::string& message) {
    std::cerr << "[NativeLog] " << message << std::endl;
}

void throwJavaException(JNIEnv* env, const char* message) {
    jclass exceptionCls = env->FindClass("java/lang/RuntimeException");
    if (exceptionCls != nullptr) {
        env->ThrowNew(exceptionCls, message);
    }
}

extern "C" {

    JNIEXPORT jstring JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegGetVersion
    (JNIEnv* env, jclass clazz) {
        try {
            std::cout << "passport seg get version........" << std::endl;
            const char* version = passport_seg_get_version();
            return env->NewStringUTF(version);
        }
        catch (const std::exception& e) {
            native_log(e.what());
            throwJavaException(env, e.what());
            return env->NewStringUTF("error");
        }
        catch (...) {
            native_log("Unknown error in passportSegGetVersion");
            throwJavaException(env, "Unknown error in passportSegGetVersion");
            return env->NewStringUTF("error");
        }
    }

    JNIEXPORT jint JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegInit
    (JNIEnv* env, jclass clazz, jint equipType, jint passportType, jstring modelPath, jstring configPath) {
        try {
            const char* model_path = env->GetStringUTFChars(modelPath, 0);
            const char* config_path = env->GetStringUTFChars(configPath, 0);

            int result = passport_seg_init(equipType, passportType, model_path, config_path);

            env->ReleaseStringUTFChars(modelPath, model_path);
            env->ReleaseStringUTFChars(configPath, config_path);
            return result;
        }
        catch (const std::exception& e) {
            native_log(e.what());
            throwJavaException(env, e.what());
            return -1;
        }
        catch (...) {
            native_log("Unknown error in passportSegInit");
            throwJavaException(env, "Unknown error in passportSegInit");
            return -1;
        }
    }

    JNIEXPORT jstring JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegRun
    (JNIEnv* env, jclass clazz, jstring jsonStr, jstring saveRoot, jintArray codeArray) {
        try {
            if (!jsonStr || !saveRoot || !codeArray) {
                native_log("Null input detected.");
                throwJavaException(env, "Null input parameter.");
                return nullptr;
            }
            const char* json_str = env->GetStringUTFChars(jsonStr, 0);
            const char* save_root = env->GetStringUTFChars(saveRoot, 0);
            jint* codes = env->GetIntArrayElements(codeArray, NULL);

            if (!json_str || !save_root || !codes) {
                native_log("Failed to convert jstring/jintArray.");
                throwJavaException(env, "JNI conversion failed.");
                return nullptr;
            }
            native_log("Calling passport_seg_run...");
            const char* result = passport_seg_run(json_str, save_root, reinterpret_cast<int*>(codes));

            jstring jResult = nullptr;
            if (result != nullptr) {
                jResult = env->NewStringUTF(result); // 拷贝内容
                passport_seg_release_per_call(result); // 安全释放
            }
            else {
                jResult = env->NewStringUTF("");
            }

            env->ReleaseStringUTFChars(jsonStr, json_str);
            env->ReleaseStringUTFChars(saveRoot, save_root);
            env->ReleaseIntArrayElements(codeArray, codes, 0);

            return jResult;
        }
        catch (const std::exception& e) {
            native_log(std::string("[C++ Exception] ") + e.what());
            throwJavaException(env, e.what());
            return nullptr;
        }
        catch (...) {
            native_log("Unknown error in passportSegRun");
            throwJavaException(env, "Unknown error in passportSegRun");
            return nullptr;
        }
    }

    JNIEXPORT void JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegReleasePerCall
    (JNIEnv* env, jclass clazz, jstring jsonCtr) {
        try {
            const char* json_ctr = env->GetStringUTFChars(jsonCtr, 0);
            passport_seg_release_per_call(json_ctr);
            env->ReleaseStringUTFChars(jsonCtr, json_ctr);
        }
        catch (const std::exception& e) {
            native_log(e.what());
            throwJavaException(env, e.what());
        }
        catch (...) {
            native_log("Unknown error in passportSegReleasePerCall");
            throwJavaException(env, "Unknown error in passportSegReleasePerCall");
        }
    }

    JNIEXPORT void JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegRelease
    (JNIEnv* env, jclass clazz) {
        try {
            passport_seg_release();
        }
        catch (const std::exception& e) {
            native_log(e.what());
            throwJavaException(env, e.what());
        }
        catch (...) {
            native_log("Unknown error in passportSegRelease");
            throwJavaException(env, "Unknown error in passportSegRelease");
        }
    }

}

建议添加异常处理,避免JVM 崩溃。

五、Visual Studio 配置

1. 添加包含目录

打开【项目属性】 > C/C++ > 常规 > 附加包含目录

防止编译阶段报错。

2. 添加库目录

打开【链接器】 > 常规 > 附加库目录

3. 添加依赖库

打开【链接器】 > 输入 > 附加依赖项

告诉编译器如何调用 .dll 中的函数;

4.生成 DLL

编译后会生成 passportSeg.dllpassportSeg.lib,将他们复制到 jdk 的 bin 目录或者你自己定义的目录下, java 就可以直接调用 passportSeg了。

六、Java 调用测试

java 测试结果是否正确,也可以自己封装成接口。

public static void main(String[] args) {
    System.out.println("版本号>>>>" + PassportSeg.passportSegGetVersion());

    int init = PassportSeg.passportSegInit(1, 1, MODEL_PATH, CONFIG_PATH);
    if (init != 0) {
        System.out.println("Init failed: " + init);
        return;
    }

    int[] code = new int[1];
    String resultJson = PassportSeg.passportSegRun(IMG_PATH, SAVE_PATH, code);

    System.out.println("Result: " + resultJson);
    System.out.println("Error Code: " + code[0]);
     // JNI 层已自动调用,这里不需要重复调用
    // PassportSeg.passportSegReleasePerCall(resultJson);
    PassportSeg.passportSegRelease();
}

调试建议

如果报错找不到 passportSeg.dll,请将该 DLL 放入:

  • 项目运行目录;
  • 或者 jdk/bin 目录;
  • 或者设置 -Djava.library.path

如果 JNI 函数名对应不上,请确保:

  • 包名、类名、方法名匹配;
  • DLL 导出的函数使用 extern "C"
  • JNI 方法未正确导出,可通过.def文件显式指定导出符号。

七、总结

  • 编写 Java native 接口;
  • 使用 javah 生成 JNI 头文件;
  • C++ 实现 JNI 方法;
  • 配置 Visual Studio 编译动态库;
  • Java 调用测试。

如果你不想污染自己的JDK bin,可以将 .dll.lib 放到一个统一的目录下,这样的话切换 jdk 比较方便,因为我只使用 jdk8,所以贪图方便放在了 bin 目录,但这是不规范滴 。

到此这篇关于Java调用C++动态库(DLL)的完整实践指南的文章就介绍到这了,更多相关Java调用C++动态库DLL内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mybatis的resultMap返回map问题

    Mybatis的resultMap返回map问题

    这篇文章主要介绍了Mybatis的resultMap返回map问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • 实例解析Java关于static的作用

    实例解析Java关于static的作用

    只要是有学过Java的都一定知道static,也一定能多多少少说出一些作用和注意事项。文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • logback ThresholdFilter临界值日志过滤器源码解读

    logback ThresholdFilter临界值日志过滤器源码解读

    这篇文章主要为大家介绍了logback ThresholdFilter临界值日志过滤器源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • Spring boot整合security详解

    Spring boot整合security详解

    Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架,本文主要介绍了SpringBoot整合Security安全框架的方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07
  • SpringBoot 2.x 整合Lombok的方法示例

    SpringBoot 2.x 整合Lombok的方法示例

    Spring Boot是非常高效的开发框架,lombok是一套代码模板解决方案,将极大提升开发的效率,这篇文章主要介绍了SpringBoot 2.x 整合Lombok的方法示例,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • 图解Java ReentrantLock公平锁和非公平锁的实现

    图解Java ReentrantLock公平锁和非公平锁的实现

    ReentrantLock是Java并发中十分常用的一个类,具备类似synchronized锁的作用。但是相比synchronized, 它具备更强的能力,同时支持公平锁和非公平锁。本文就来聊聊ReentrantLock公平锁和非公平锁的实现,需要的可以参考一下
    2022-10-10
  • feign开启日志Logger.Level feignLoggerLevel()中Level爆红的解决

    feign开启日志Logger.Level feignLoggerLevel()中Level爆红的解决

    这篇文章主要介绍了feign开启日志Logger.Level feignLoggerLevel()中Level爆红的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Java synchronized最细讲解

    Java synchronized最细讲解

    synchronized是Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。本文给大家介绍java中 synchronized的用法,对本文感兴趣的朋友一起看看吧
    2021-09-09
  • SpringBoot同时集成Mybatis和Mybatis-plus框架

    SpringBoot同时集成Mybatis和Mybatis-plus框架

    在实际开发中,项目里面一般都是Mybatis和Mybatis-Plus公用,但是公用有版本不兼容的问题,本文主要介绍了Spring Boot项目中同时集成Mybatis和Mybatis-plus,具有一档的参考价值,感兴趣的可以了解一下
    2024-12-12
  • CompletableFuture并行处理List分批数据demo

    CompletableFuture并行处理List分批数据demo

    这篇文章主要介绍了CompletableFuture并行处理List分批数据实现实例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11

最新评论