在 Java 專案中整合原生程式庫(Windows 下的 .dll、Linux 下的 .so)是許多開發者面臨的挑戰。本文系統梳理 JNI 本地庫載入的核心機制、常見錯誤原因,並詳細對比標準 Java 應用與 Eclipse RCP 平台的不同載入策略,提供經實戰驗證的最佳實踐方案。
理解依賴鏈是解決 UnsatisfiedLinkError 的關鍵:
System.loadLibrary() 或 System.load() 直接載入的庫(如 TImage.dll)TImage.dll 依賴 TSealSvrDll.dll、yhocr.dll 等)核心原則:被依賴的庫必須先於依賴它的庫載入,否則 JVM 會拋出 UnsatisfiedLinkError。
| 方法 | 參數要求 | 適用場景 |
|---|---|---|
System.loadLibrary(String libName) | 僅庫名稱(不含前綴 lib、後綴 .dll/.so),需在 java.library.path 指定的目錄中 | 標準部署,庫放在系統路徑或自定義路徑 |
System.load(String absolutePath) | 庫檔案的絕對路徑(含檔名) | 庫位置固定、不想修改環境變數、或庫打包在 JAR 內需解壓到臨時目錄 |
錯誤做法:在程式碼中呼叫 System.setProperty("java.library.path", "...") —— 無效! 此屬性在 JVM 啟動時唯讀。
正確做法:
java -Djava.library.path=/path/to/libs -jar app.jar-Djava.library.path=...application.ini,在 -vmargs 後追加;,Linux/macOS 用冒號 :/natives/win32/)Files.createTempDirectory())java.library.path(需在 JVM 啟動前)或直接用 System.load(absolutePath)Eclipse 的 BundleClassLoader 內建支援插件內本地庫載入,無需修改 java.library.path、無需手動解壓。
在插件根目錄建立以下任一目錄結構(依平台/架構):
plugin-root/
├── os/
│ └── win32/
│ └── x86_64/ 或 x86/ ← 64/32 位元 Windows
│ └── linux/
│ └── x86_64/ ← Linux 64 位元
├── ws/
│ └── win32/ ← 舊版相容
└── nl/
└── zh_TW/ ← 語言相關資源
my.plugin/
├── .classpath
├── .project
├── build.properties
├── classes/
├── lib/
├── META-INF/
│ └── MANIFEST.MF
├── os/
│ └── win32/
│ └── x86_64/
│ ├── TImage.dll
│ ├── yhocr.dll
│ └── ... (所有依賴庫)
├── plugin.xml
└── src/
Bundle-NativeCode: os/win32/x86_64/*.dll;
osname=win32; processor=x86_64,
os/linux/x86_64/*.so;
osname=linux; processor=x86_64
此標頭讓 Eclipse 自動根據運行平台選擇正確架構的庫載入。
public class NativeLoader {
static {
// 無需指定路徑,BundleClassLoader 自動尋找
System.loadLibrary("TImage");
}
// native 方法宣告...
}
System.load() 失敗,建議路徑純 ASCII標準 Java 應用靈活控制載入路徑但配置繁瑣;Eclipse RCP 透過 BundleClassLoader 與 Bundle-NativeCode 實現了「約定優於配置」,大幅降低本地庫整合複雜度。選擇適合專案架構的方案,並嚴守依賴順序與架構一致性,是穩定載入本地庫的關鍵。