在 Web 專案中調用 JNI(Java Native Interface)時,常遇到一個經典問題:當應用容器(如 Tomcat)因類文件變更而熱重載 Context 時,JNI 類會嘗試再次加載同一個動態庫,導致 UnsatisfiedLinkError,使該功能完全不可用。
public class SystemInfo {
static {
System.loadLibrary("SystemInfo"); // 載入本地庫
}
public static native long getPhysicalMemory();
}
static 區塊中的 System.loadLibrary,JVM 檢測到庫已存在,拋出異常。核心思路:讓 JNI 類由容器的共享 ClassLoader 載入,而非 Web 應用專屬的 ClassLoader。這樣 Context 重載時,共享 ClassLoader 不會被銷毀,JNI 類也不會重複初始化。
SystemInfo.class)單獨打包成 JAR(例如 jni-common.jar)。{tomcat_home}/lib/(Tomcat 7+)或 {tomcat_home}/common/lib/(舊版本)。WEB-INF/classes 或 WEB-INF/lib 中移除該 JNI 類/JAR。Tomcat 的類加載層級(由父到子):
Bootstrap ClassLoader (JRE 核心庫)
↓
System ClassLoader (Tomcat 啟動類路徑)
↓
Common ClassLoader ({tomcat}/lib/*.jar) ←←← 放這裡!
↓
Webapp ClassLoader (WEB-INF/classes, WEB-INF/lib/*.jar) ←←← 不要放這裡
Common ClassLoader 生命週期隨容器啟動/關閉,不受 Web 應用重載影響。
JNI 庫無法重複載入是 Java 原生限制。通過調整類加載層級,將 JNI 類提升到容器共享 ClassLoader,即可徹底解決熱重載導致的庫加載衝突。這是部署 JNI Web 應用的標準最佳實踐。