前言
什么是JNA?有哪些特性?工作原理?应用场景?使用方法?
一、JNA是什么?
- 定义:
JNA(Java Native Access)是一个开源的Java框架,建立在JNI(Java Native Interface,Java本地接口)技术之上。它提供了一组Java工具类,使得Java代码能够在运行时动态地访问系统本地库(Native Library),如Windows的动态链接库(.dll)和Linux的共享库(.so),而无需编写任何Native/JNI代码。 - 工作原理: JNA的工作原理可以简单理解为提供一个“桥梁”,使Java代码能够直接访问动态链接库中的函数。开发人员只需在Java接口中描述目标本地库的函数与结构,JNA就会自动实现Java接口到Native Library的映射。这样,
调用本地方法就如同调用普通Java方法一样便捷。 - 优点:
1)
简化开发:使用JNA后,开发人员无需编写繁琐的JNI代码,只需在Java接口中描述目标Native Library的函数与结构即可。 2)跨平台:JNA支持多种操作系统,如Windows、Linux等,使得Java代码能够更方便地访问不同操作系统上的本地库。 3)动态加载:JNA允许在运行时动态加载本地库,这提高了程序的灵活性和可扩展性。 - 应用场景:
1)
调用本地库函数:Java程序需要调用本地库中的函数时,可以使用JNA来简化调用过程。 2)跨语言通信:Java与其他编程语言(如C/C++)进行通信时,JNA可以作为一个方便的桥梁。 3)性能优化:某些情况下,使用本地库可以提高程序的性能。通过JNA,Java程序可以方便地调用这些高性能的本地库函数。
二、使用步骤
1.引入依赖
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.15.0</version>
</dependency>
2.开发模板
三方本地方法库(.dll或.so)引入、实现、调用过程开发模板及规约(增强模板复用和开发规范) 1、设置三方本地方法库枚举类型,约定好各字段默认常量值。 2、根据定义的枚举属性创建文件夹,根据层级关系引入动态库文件。 3、创建三方库对应的接口,此接口必须继承Library。 4、创建三方业务handler并继承类库加载器,同时可以实现个性化定制方法或功能。
代码如下(示例):
package com.onefish.toolkit.natives.support;
/**
* 三方本地方法库枚举类型
*
* @author onefish
*/
public enum CustomNativeEnum {
ONE_FISH("一只鱼科技有限公司", "1", "one-fish", "biz");
private String customName;
private String customValue;
private String customFolder;
private String customLibName;
CustomNativeEnum() {
}
CustomNativeEnum(String customName, String customValue, String customFolder, String customLibName) {
this.customName = customName;
this.customValue = customValue;
this.customFolder = customFolder;
this.customLibName = customLibName;
}
public String getCustomLibName() {
return customLibName;
}
public void setCustomLibName(String customLibName) {
this.customLibName = customLibName;
}
public String getCustomFolder() {
return customFolder;
}
public void setCustomFolder(String customFolder) {
this.customFolder = customFolder;
}
public String getCustomValue() {
return customValue;
}
public void setCustomValue(String customValue) {
this.customValue = customValue;
}
public String getCustomName() {
return customName;
}
public void setCustomName(String customName) {
this.customName = customName;
}
}
package com.onefish.toolkit.natives.support;
import java.util.Objects;
/**
* 三方本地方法库复合键(库路径 + 接口类)
*
* @author onefish
*/
public class LibraryKey {
// 进行hash计算的字段不可变,否则会导致对象存入hash表后无法检索到
private final String libPath;
private final Class<?> interfaceClass;
public LibraryKey(String libPath, Class<?> interfaceClass) {
this.libPath = libPath;
this.interfaceClass = interfaceClass;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LibraryKey that = (LibraryKey) o;
return Objects.equals(libPath,that.libPath) &&
Objects.equals(interfaceClass,that.interfaceClass);
}
@Override
public int hashCode() {
// 自定义hash哈希码生成算法,考虑哈希冲突、哈希分布、性能高低。
// 31 是奇质数,且 31 * i 可优化为 (i << 5) - i,提升计算速度。
return 31 * libPath.hashCode() + interfaceClass.hashCode();
}
}
package com.onefish.toolkit.natives.support;
import com.sun.jna.Library;
import com.sun.jna.Native;
import java.util.concurrent.ConcurrentHashMap;
public class LibraryPool {
private static final ConcurrentHashMap<LibraryKey, Library> LIBS_POOL = new ConcurrentHashMap<>();
/**
* 加载或获取已缓存的 JNA 接口实例
*
* @param libPath 动态库路径(如 "/one-fish/linux-x64/libbiz.so")
* @param interfaceClass JNA 接口类(需继承 Library)
* @return 已加载的接口实例
*/
@SuppressWarnings("unchecked")
public static <T extends Library> T load(String libPath, Class<T> interfaceClass) {
LibraryKey key = new LibraryKey(libPath, interfaceClass);
return (T) LIBS_POOL.computeIfAbsent(key, k ->
Native.load(libPath, interfaceClass)
);
}
}
package com.onefish.toolkit.natives.support;
import com.sun.jna.Library;
import java.util.concurrent.ConcurrentHashMap;
/**
* 三方本地方法动态库抽象泛型加载器
*
* @author onefish
*/
public abstract class AbstractNativeLibraryLoader<T> {
private static final ConcurrentHashMap<String, String> ARCH = new ConcurrentHashMap<>();
// 区分不同架构
static {
ARCH.put("x86_64", "x64");
ARCH.put("x86", "x86");
ARCH.put("amd64", "x64");
ARCH.put("arm64", "aarch64");
ARCH.put("aarch64", "aarch64");
ARCH.put("mips64el", "mips64el");
ARCH.put("sw_64", "sw64");
ARCH.put("sw64", "sw64");
ARCH.put("loongarch64", "loongarch64");
}
private String libPath;
private CustomNativeEnum customType;
public AbstractNativeLibraryLoader() {}
/**
* 获取 cpu架构
*
* @return 架构
*/
public static String obtainCpuArch() {
return ARCH.getOrDefault(System.getProperty("os.arch"), "unknown");
}
/**
* 判断平台类型(不考虑 mac)
*
* @return true or false
*/
public static boolean judgeIsLinux() {
return System.getProperty("os.name").toLowerCase().contains("linux");
}
/**
* 通用模板方法
*/
public void generateTemplateStruct() {
// 定义三方枚举类型
this.customType = setTripartite();
// 生成三方本地方法库路径
this.libPath = generateLibPath(this.customType);
// 自定义业务逻辑
customBizLogic();
}
/**
* 加载三方本地方法库
*
* @param libPath 库路径
* @param interfaceClass 库接口类型
* @return T 库接口实例
*/
public <T extends Library> T load(String libPath, Class<T> interfaceClass) {
return LibraryPool.load(libPath, interfaceClass);
}
protected abstract CustomNativeEnum setTripartite();
protected void customBizLogic() {
}
private String generateLibPath(CustomNativeEnum customNativeEnum) {
if (customNativeEnum == null) throw new NullPointerException("customNativeEnum is null");
String arch = obtainCpuArch();
String archFolder = generateArchFolder(arch);
String archFile = generateArchFile(customNativeEnum.getCustomLibName(), arch);
String customFolder = customNativeEnum.getCustomFolder();
// 形式如:one-fish/linux-x64/libbiz.so
return String.format("%s%s%s%s%s", customFolder, "/", archFolder, "/", archFile);
}
private String generateArchFolder(String arch) {
return judgeIsLinux() ? "linux-" + arch : "windows-" + arch;
}
private String generateArchFile(String libPrefName, String arch) {
return judgeIsLinux() ? "lib" + libPrefName + "_" + arch + ".so" : libPrefName + "_" + arch + ".dll";
}
public String getLibPath() {
return libPath;
}
public void setLibPath(String libPath) {
this.libPath = libPath;
}
public CustomNativeEnum getCustomType() {
return customType;
}
public void setCustomType(CustomNativeEnum customType) {
this.customType = customType;
}
}
package com.onefish.toolkit.natives.custom;
import com.sun.jna.Library;
/**
* 三方业务接口 (方法签名,返回值,形参列表)
*
* @author onefish
*/
public interface CustomLibrary extends Library {
// 两数加和运算
int add(int a, int b);
}
package com.onefish.toolkit.natives.custom;
import com.onefish.toolkit.natives.support.AbstractNativeLibraryLoader;
import com.onefish.toolkit.natives.support.CustomNativeEnum;
import com.sun.jna.Library;
/**
* 三方业务处理器
*
* @author onefish
*/
public class CustomHandler extends AbstractNativeLibraryLoader<Library> {
private static final CustomHandler INSTANCE = new CustomHandler();
public CustomHandler() {
// 拒绝反射方式创建实例,保证单例完整性
if (INSTANCE != null) throw new IllegalStateException("CustomHandler already initialized");
super.generateTemplateStruct();
}
public static CustomHandler getInstance() {
return INSTANCE;
}
/**
* 定制算法逻辑
*
* @param a 形参1
* @param b 形参2
* @return 5倍加和
*/
public static int customAdd(int a, int b) {
CustomHandler customHandler = getInstance();
String libPath = customHandler.getLibPath();
CustomLibrary customLibrary = customHandler.load(libPath, CustomLibrary.class);
return 5 * customLibrary.add(a, b);
}
public CustomNativeEnum getNativeEnum() {
return CustomNativeEnum.ONE_FISH;
}
@Override
protected CustomNativeEnum setTripartite() {
return getNativeEnum();
}
}
总结
上面只是给出了简单的实现思路,对于Java跨语言调用还需要考虑性能和稳定性等,比如中间层jna如何降低内存占用,提高加载效率?若在执行本地方法库方法时出现段错误等严重错误时,上层Java应用如何容错处理保证自身稳定性不受影响?