在一些Java應用中需要實用JNI來調用DLL動態鏈接庫,但是,如果做成模塊打成JAR包,一般情況是無法直接載入DLL的。
常用的DLL載入方式如下
- //加載test.dll
- System.loadLibrary("test");
- System.load("test");
需要說明的是加載DLL的有效路徑與一個JVM中的系統變量有關,可以用以下方法查看該變量。
- System.getProperty("java.library.path");
在該變量中含有當前路徑".",需要特別說明,這個當前路徑是執行時的當前路徑,舉例來說。
- //當前路徑在C:根目錄下,而執行的程序在C:/app/Test.class
- C:\> java app/Test //這時的 . 表示的是C:\
- C:\app> java Test //這時的 . 表示的是C:\app
接下來就是重點了,DLL已經打包到了JAR中,比如打進了APP.jar,那對於Windows來說,這個DLL不存在於任何的目錄中,自然無法正常調用到。那解決方法就是在執行的時候將JAR包中的DLL解到某個地方,然后再載入。因為使用下面的代碼可以將JAR包中的任何文件作為資源載入。
- MyClass.class.getResourceAsStream(dllName); MyClass.class.getResource(dllName);
這樣就可以在JAR包執行時把DLL讀取出來解在一個地方了,我寫了一個方法來代替loadLibrary,DLL會被放入系統的臨時文件夾中,在那里會被載入,synchronized保證該過程在多線程下安全,也許在將來可以優化。
- //BIN_LIB為JAR包中存放DLL的路徑
- //getResourceAsStream以JAR中根路徑為開始點
- private synchronized static void loadLib(String libName) throws IOException {
- String systemType = System.getProperty("os.name");
- String libExtension = (systemType.toLowerCase().indexOf("win")!=-1) ? ".dll" : ".so";
- String libFullName = libName + libExtension;
- String nativeTempDir = System.getProperty("java.io.tmpdir");
- InputStream in = null;
- BufferedInputStream reader = null;
- FileOutputStream writer = null;
- File extractedLibFile = new File(nativeTempDir+File.separator+libFullName);
- if(!extractedLibFile.exists()){
- try {
- in = SMAgent.class.getResourceAsStream(BIN_LIB + libFullName);
- if(in==null)
- in = SMAgent.class.getResourceAsStream(libFullName);
- SMAgent.class.getResource(libFullName);
- reader = new BufferedInputStream(in);
- writer = new FileOutputStream(extractedLibFile);
- byte[] buffer = new byte[1024];
- while (reader.read(buffer) > 0){
- writer.write(buffer);
- buffer = new byte[1024];
- }
- } catch (IOException e){
- e.printStackTrace();
- } finally {
- if(in!=null)
- in.close();
- if(writer!=null)
- writer.close();
- }
- }
- System.load(extractedLibFile.toString());
- }