java调用动态库dll/so(三)jna爬坑指南+设计自己的加载动态库框架

  • 一、加载的前提准备

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

  1. 动态库本身使用32位编译器编译,则只能在32操作系统上加载成功;同理,若动态库本身为64位编译器编译,则只能在64位操作系统上加载成功;
  2. 动态库本身所需的一级依赖必须在本机上具有,不缺失,即:不缺少依赖;
  3. 若在windows上加载,所需要加载动态库依赖的第三方动态库中缺少系统api,如api-ms-win-core-xxx.dll这些库,可不用理会,不影响加载,但运行时可根据接口使用情况进行判断是否需要解决缺失依赖;
  4. 在使用Native.load(String name, Class 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> clazzs = ClassUtil.getClasses(LIBPACKAGE, LibLoad.class, false);
		if (clazzs != null) {
			for (Class dllclass : clazzs) {
				Library libtmp = null;
				@SuppressWarnings("unchecked")
				Class clazzLib = (Class) 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://download.csdn.net/download/fanpei_moukoy/11195072

                         https://github.com/maokofan/maoko.dllSoLibLoad

 

你可能感兴趣的:(C#/C++,Java)