簡體   English   中英

在初始化靜態對象之前,如何使Classloader評估類路徑?

[英]How to Make a Classloader evaluate the classpath before initlializing static objects?

我有一個正在開發的應用程序,它同時使用jar庫和本機系統庫。 我的問題是默認的類加載器會在調用main之前很久就嘗試加載靜態類。 由於類路徑尚未包含靜態類所需的本機庫,因此在第一個評估的靜態引用處將引發java.lang.NoClassDefFoundError。

這是我無法訪問的庫加載方法的樣子:

private static void load_libraries() {
    try {
        String osname = getOSName();

        if (osname == null) {
            throw new RuntimeException("The system you are running on is not supported");
        }
        URL u = NativeLibs.class.getResource(osname);

        URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
        Class urlClass = URLClassLoader.class;
        Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
        method.setAccessible(true);
        method.invoke(urlClassLoader, new Object[]{u});
    } catch (IllegalAccessException ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to load native libs", ex);
    } catch (IllegalArgumentException ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to load native libs", ex);
    } catch (InvocationTargetException ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to load native libs", ex);
    } catch (NoSuchMethodException ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to load native libs", ex);
    } catch (SecurityException ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to load native libs", ex);
    }

}

所以,讓我解釋一下這是怎么回事。 首先,getOSName()所做的所有操作都是返回一個表示我正在運行的OS系列的字符串。 如果操作系統不受支持,則返回null。 萬一我在該方法上遇到問題,我將繼續進行發布:

private static String getOSName() {
    String os = System.getProperty("os.name").toLowerCase(Locale.US);
    if (os.indexOf("win") >= 0) {
        return "windows";
    } else if (os.indexOf("mac os x") >= 0) {
        return "macosx";
    } else if (os.indexOf("nux") >= 0) {
        return "linux";
    } else if (os.indexOf("solaris") >= 0) {
        return "solaris";
    } else {
        return null;
    }
}

它返回的字符串是目錄名,方法load_libraries()使用該目錄名定義一個相對URL,我以反射方式傳遞給類加載器以加載庫。 我的問題是,即使這是main中的第一個方法,運行時執行也永遠不會到達load_libraries()。

一種顯而易見的解決方案是使用自定義的jar靜態地加載本機庫。 我根本不想靜態鏈接本機庫,因為這違背了Java平台可移植性的目的。 我已經推斷出的另一個解決方案是一個系統可執行文件,它可以使用來自系統可執行文件的確定的自動類路徑來運行應用程序jar,但是該解決方案仍需要多個特定於平台的可執行文件。

所以,這是我的問題:

我可以強制類加載器在初始化靜態對象之前運行我的庫加載方法嗎? 這是否需要定制的ClassLoader? 如果確實需要自定義ClassLoader,是否只需傳遞代表默認Classloader的系統屬性即可解決此問題?

謝謝,我希望我寫了足夠詳細的問題!

“默認類加載器會在調用main之前很久就嘗試加載靜態類”的原因是因為您的主類具有靜態成員變量和/或靜態初始化器。 當類加載器初始化您的主類時,它還必須初始化這些靜態變量。 有關更多信息,請參見VM Spec

解決方案(唯一的方法)是消除靜態初始化。 main()初始化靜態成員變量,或使用諸如Spring之類的框架進行初始化。

通常,靜態成員變量很少有很好的用途。 他們中的大多數最終都是黑客,他們可以從程序中的任意位置訪問資源。 這不僅使程序更難測試,而且導致意外的初始化鏈。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM