欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

jna使用之(三)java调用动态库dll/so-设计自己的加载动态库框架

程序员文章站 2022-05-28 12:02:33
...
  • 一、加载的前提准备

加载动态库之前需要明白一下几个避坑点:

  1. 动态库本身使用32位编译器编译,则只能在32操作系统上加载成功;同理,若动态库本身为64位编译器编译,则只能在64位操作系统上加载成功;
  2. 动态库本身所需的一级依赖必须在本机上具有,不缺失,即:不缺少依赖;
  3. 若在windows上加载,所需要加载动态库依赖的第三方动态库中缺少系统api,入api-ms-win-core-xxx.dll这些库,可不用理会,不影响加载,但运行时可根据接口使用情况进行判断是否需要解决缺失依赖;
  4. 在使用Native.load(String name, Class<T> interfaceClass)加载动态库时,name变量不需要加.so或者.dll后缀,且若是linux下加载.so动态库,lib前缀也省略,例如需要加载libCTest.so这个库,则name=CTest即可。(原因是源码内部根据操作系统类型已自动帮你添加lib前缀,自动填充了.so、.dll后缀)
  • 二、依赖解决方式

依赖的解决有两种:

  1. 将依赖拷贝至系统环境bin目录下并注册,具体的位置以windows举例,32位拷贝至C:\Windows\System32下,64位拷贝至C:\Windows\SysWOW64\downlevel。(作参考,可谷歌,未实践此方法);
  2. 将依赖拷贝至动态库同级目录下。(本文采用此方式)

查看依赖的工具可参考之前的文章:jna使用之(一)java调用动态库dll/so-知识准备

  • 三、jna加载动态库路径问题

jna加载动态库具有多种方式,主要有以下三种:

(1).依赖库在项目的\target\classes下,即,建立工程时,将依赖库拷贝至在maven工程的src/main/resources;

(2).在maven工程的src/main/resources下建立系统文件夹名,并拷贝依赖至此,这里只列举linux和windows下文件夹名称情况。

系统 位数 文件夹名
linux 32 linux-x86
64 linux-x86-64
windows 32 win32-x86
64 win32-x86-64

(3).指定路径模式,此方法需要配置动态库根目录,即"jna.library.path"环境变量(本文采用此方式)。

  • 四、加载框架实现

我的加载框架实现主要由注解、接口、加载实现三个部分完成,在实际开发过程中,用户只需要定义接口,并给接口上使用我们定义的注解,并拷贝动态库之指定位置即可。具体代码如下解析:

1.注解

linuxNam和windowsName指的是动态库在两个系统下的名称,无前后缀。

package maoko.dllSolibLoad.lib.load;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * lib接口注解
 * 
 * @author maoko
 *
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LibLoad {
	/**
	 * linux lib 名称
	 * 
	 * @return
	 */
	String linuxName() default "";

	/**
	 * windows lib 名称
	 * 
	 * @return
	 */
	String windowsName() default "";
}

2.加载实现-JnaLibCall 

使用到了反射扫描指定的LIBPACKAGE包下的接口,获取定义注解LibLoad的动态库名称,进行加载;在函数setJnaLibPath()中预配置jna.library.path系统属性,使jna在指定的地方进行加载.so/.dll。

package maoko.dllSolibLoad.lib.load;

import java.util.Set;

import com.sun.jna.Library;
import com.sun.jna.Native;

import maoko.common.ClassUtil;
import maoko.common.file.PathUtil;
import maoko.common.log.IWriteLog;
import maoko.common.log.Log4j2Writer;
import maoko.common.model.enm.EOsType;
import maoko.common.system.AppRunPathUitl;
import maoko.common.system.OSPlatformUtil;

/**
 * lib动态库调用方法:dll/so file mast have lib prefix
 * 
 * @author fanpei
 * @date 2019年5月22日下午4:49:43
 */
public class JnaLibCall {
	private static final String LIBPACKAGE = "maoko.dllSolibLoad.lib.load.ifs";// 接口目录,根据自己的动态库接口所在包名进行修改

	private static final IWriteLog log = new Log4j2Writer(JnaLibCall.class);

	private static final String JNA_LIBRARY_PATH = "jna.library.path";
	private static final String WINDOWS_LIB_PATH = "lib/windows";// local dll file location
	private static final String LINUX_LIB_PATH = "lib/linux"; // local so file location
	private static final String WINDOWS_SEPRATOR = ";";
	private static final String LINUX_SEPRATOR = ":";

	/**
	 * 加载
	 * 
	 * @throws Exception
	 */
	public static void load() throws Exception {

		EOsType ostype = OSPlatformUtil.getOSType();
		setJnaLibPath(ostype);
		// 加载dll
		Set<Class<?>> clazzs = ClassUtil.getClasses(LIBPACKAGE, LibLoad.class, false);
		if (clazzs != null) {
			for (Class<?> dllclass : clazzs) {
				Library libtmp = null;
				@SuppressWarnings("unchecked")
				Class<Library> clazzLib = (Class<Library>) dllclass;
				LibLoad dllName = dllclass.getAnnotation(LibLoad.class);
				try {
					String libname = "";
					if (EOsType.Linux == ostype) {
						libname = dllName.linuxName();
					} else// windows
					{
						libname = dllName.windowsName();
					}
					libtmp = Native.load(libname, clazzLib);
					LibFactory.add(dllclass.getName(), libtmp);// 加入自定义库工厂,用于后续根据名字调用
					log.info("loading lib:{} sucessful", dllclass.getName());
				} catch (Throwable e) {
					log.warn("load lib file faied:{}", e);
				}
			}
			log.info("loaded lib count:{}", LibFactory.totalLib());
		}
	}

	/**
	 * 设置jna加载指定路径
	 * 
	 * @param ostype 系统类型
	 */
	private static void setJnaLibPath(EOsType ostype) {
		String appPath = AppRunPathUitl.getAppRunPath();
		String libRootPath = "";
		String libPath = "";
		// String nameEnd = "";
		String seprator = "";
		if (EOsType.Linux == ostype) {
			libPath = LINUX_LIB_PATH;
			// nameEnd = LINUX_DLL;
			seprator = LINUX_SEPRATOR;
		} else// windows
		{
			libPath = WINDOWS_LIB_PATH;
			// nameEnd = WINDOWS_DLL;
			seprator = WINDOWS_SEPRATOR;
		}
		libRootPath = PathUtil.combinePath(appPath, libPath);
		String jnaPath = System.getProperty(JNA_LIBRARY_PATH);
		System.err.println("jna.library.path:" + jnaPath);
		if (jnaPath == null) {
			jnaPath = libRootPath;
		} else if (!jnaPath.contains(libRootPath))
			jnaPath = jnaPath + seprator + libRootPath;
		System.setProperty(JNA_LIBRARY_PATH, jnaPath);
		System.setProperty("jna.debug_load", "true");//启用jna调试日志输出
		System.err.println("the latest jna.library.path:" + System.getProperty(JNA_LIBRARY_PATH));
	}

}

代码完整地址:https://github.com/maokofan/maoko.dllSoLibLoad

相关标签: jna 加载动态库