Java 调用 DLL 动态库

1. 项目背景

在 Windows 平台上,很多底层库或第三方 API 以 DLL(Dynamic Link Library,动态链接库) 形式提供。Java 本身不能直接调用 DLL,需要使用 JNI(Java Native Interface,Java 本地接口)JNA(Java Native Access) 进行调用。

本项目主要介绍 如何使用 Java 调用 DLL 文件,并提供 JNI 和 JNA 两种方式 的实现,帮助你根据需求选择合适的方法。


2. 相关知识

2.1 什么是 DLL?

  • DLL(Dynamic Link Library,动态链接库) 是 Windows 操作系统中的共享库,多个程序可以动态加载 DLL 以实现代码复用。
  • DLL 里面通常包含 函数、类、资源等,可用于扩展软件功能。

2.2 Java 如何调用 DLL?

Java 不能直接调用 C/C++ 编写的 DLL,需要借助:

  1. JNI(Java Native Interface)
    • 需要编写 C/C++ 代码 作为中间层,与 Java 交互。
    • 适用于 高性能、复杂逻辑 的场景,但开发复杂。
  2. JNA(Java Native Access)
    • 不需要写 C/C++,直接使用 Java 代码调用 DLL。
    • 适用于 简单 API 调用,但性能略逊于 JNI。

2.3 使用场景

方式 适用场景 复杂度 性能
JNI 需要高性能、复杂 C++ 交互
JNA 直接调用简单 DLL 函数 较高

3. 实现思路

我们将分别使用 JNI 和 JNA 来调用 DLL:

3.1 JNI 方式

  1. 编写 Java 代码 声明本地方法。
  2. 使用 javac 生成 .class 文件,然后用 javah 生成 C 头文件。
  3. 编写 C/C++ 代码 实现 DLL 逻辑。
  4. 编译生成 DLL,让 Java 调用它。

3.2 JNA 方式

  1. 使用 JNA 库,定义接口映射 DLL 方法。
  2. 直接调用 DLL,无需额外编写 C 代码。

4. 代码实现

4.1 使用 JNI 调用 DLL

(1)编写 Java 代码
public class JniExample {
    // 声明本地方法,调用 DLL 中的 nativeMethod
    public native String nativeMethod(String input);

    static {
        System.loadLibrary("MyLibrary"); // 加载 DLL
    }

    public static void main(String[] args) {
        JniExample example = new JniExample();
        String result = example.nativeMethod("Hello JNI!");
        System.out.println("Native Method Returned: " + result);
    }
}

 

注意System.loadLibrary("MyLibrary") 加载的 DLL 名称是 MyLibrary.dll


(2)生成 C 头文件

编译 Java 代码:

javac JniExample.java

生成 JNI 头文件:

javah -jni JniExample

此时会生成 JniExample.h,内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#ifndef _Included_JniExample
#define _Included_JniExample
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JniExample
 * Method:    nativeMethod
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_JniExample_nativeMethod
  (JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
(3)编写 C 代码

创建 JniExample.c

#include "JniExample.h"
#include <stdio.h>
#include <string.h>

JNIEXPORT jstring JNICALL Java_JniExample_nativeMethod(JNIEnv *env, jobject obj, jstring input) {
    const char *inCStr = (*env)->GetStringUTFChars(env, input, NULL);
    char message[256] = "JNI Received: ";
    strcat(message, inCStr);
    (*env)->ReleaseStringUTFChars(env, input, inCStr);

    return (*env)->NewStringUTF(env, message);
}
(4)编译并生成 DLL

使用 GCC(MinGW)或 MSVC 进行编译:

gcc -shared -o MyLibrary.dll -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" JniExample.c -Wl,--add-stdcall-alias


(5)运行 Java 代码

java JniExample

输出:

Native Method Returned: JNI Received: Hello JNI!


4.2 使用 JNA 调用 DLL

(1)编写 C 代码

创建 SimpleLibrary.c

#include <stdio.h> __declspec(dllexport) int add(int a, int b) { return a + b; }

编译生成 DLL:

gcc -shared -o SimpleLibrary.dll SimpleLibrary.c


(2)编写 Java 代码
import com.sun.jna.Library;
import com.sun.jna.Native;

public class JnaExample {
    // 定义接口,映射 DLL
    public interface SimpleLibrary extends Library {
        SimpleLibrary INSTANCE = Native.load("SimpleLibrary", SimpleLibrary.class);
        int add(int a, int b);
    }

    public static void main(String[] args) {
        int result = SimpleLibrary.INSTANCE.add(5, 10);
        System.out.println("Result from DLL: " + result);
    }
}

运行 Java 代码:

java -cp .;jna-5.12.1.jar JnaExample

输出:

Result from DLL: 15


5. 代码解读

JNI 方式

  • nativeMethod:Java 声明的本地方法,在 C 代码中实现。
  • javah 生成 C 头文件,用于 C 代码编写。
  • C 代码实现:使用 JNIEnv 处理字符串并返回新的字符串。

JNA 方式

  • 不需要写 C 代码,直接使用 JNA 绑定 DLL 并调用函数。
  • 适合简单的 DLL API 交互,例如调用 数值计算、设备控制等 DLL

6. 项目总结

方式 适用场景 复杂度 优势 劣势
JNI 复杂 C/C++ 库交互,高性能场景 速度快,可完全控制 需要编写 C 代码
JNA 调用简单 DLL API 代码少,易维护 性能稍逊 JNI

选择建议:

  • 需要高性能复杂交互,用 JNI
  • 只是 调用简单 DLL 函数,用 JNA

7. 结论

本项目展示了 JNI 和 JNA 调用 DLL 的实现,适用于 Java 需要调用 Windows 本地库的场景。

Logo

纵情码海钱塘涌,杭州开发者创新动! 属于杭州的开发者社区!致力于为杭州地区的开发者提供学习、合作和成长的机会;同时也为企业交流招聘提供舞台!

更多推荐