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<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://download.csdn.net/download/fanpei_moukoy/11195072

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章