最近在做一個項目的過程中需要用到第三方的jar包和動態鏈接庫(dll),其中jar包可以直接引入,問題是在開發的時候dll可以放在System32下,
但是當項目完成後build的時候,這種方式就行不通了,所以必須考慮其他的方式來引用所需的dll文件。
我們知道,在VM參數處通過-Djava.library.path後將加載路徑指定到自己的lib目錄後,程序可以正常啓動。
但這種方式顯然不夠靈活,受限於必須從main函數啓動,並且要手動的去指定虛擬機參數。
那麼我們現在就需要採取其他的方式,比如在項目的根目錄下建一個dll的文件夾,將要用到的dll文件放到此目錄下,然後
通過System類的setProperty函數來在代碼中動態的改變一下java.library.path的值。
如下:
System.setProperty("java.library.path","%ProjectPath%/dll");
問題是這種方式是行不通的,會報錯"no JIntellitype in java.library.path"。
查找原因:
代碼中設置不起作用,主要是因爲java.library.path只在jvm啓動時讀取一次,其他情況下的修改不會起作用的。可以參考下面的這個bug:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4280189
原因和ClassLoader的實現有關係。
查看jdk的源碼會發現如下實現:
if (sys_paths == null) {
usr_paths = initializePath("java.library.path");
sys_paths = initializePath("sun.boot.library.path");
}
系統緩存了java.library.path的值,並且一直都會是第一次加載時的值。
有人提到了下面的修改方法,
if(sys_paths == null) {
sys_paths= initializePath("sun.boot.library.path");
}
usr_paths= initializePath("java.library.path");
有問題,就會有人解決問題,google一下,會發現antony_miguel在一篇文章中,使用java的反射機制,完成了對於ClassLoader類中的usr_paths變量的動態修改,
public static void addLibraryDir(String libraryPath) throws IOException {
try {
Field field = ClassLoader.class.getDeclaredField("usr_paths");
field.setAccessible(true);
String[] paths = (String[]) field.get(null);
for (int i = 0; i < paths.length; i++) {
if (libraryPath.equals(paths[i])) {
return;
}
}
String[] tmp = new String[paths.length + 1];
System.arraycopy(paths, 0, tmp, 0, paths.length);
tmp[paths.length] = libraryPath;
field.set(null, tmp);
} catch (IllegalAccessException e) {
throw new IOException(
"Failedto get permissions to set library path");
} catch (NoSuchFieldException e) {
throw new IOException(
"Failedto get field handle to set library path");
}
}
文章也同時指出了這種實現的侷限性,和jvm的實現強關聯,只要jvm實現不是用的變量usr_paths來保存java.library.path的值,這個方法就不能用了。
但是隻要知道源代碼,小小的改動就應該可以實現了。
通過調用上面的方法來將dll文件目錄加入java.library.path路徑下,然後使用System.loadLibrary("glpk_4_55")這樣的方式加載dll文件即可。
over~