Как заставить Classloader оценить путь к классам перед инициализацией статических объектов? - PullRequest
0 голосов
/ 27 марта 2011

У меня есть приложение, которое я разрабатываю и которое использует как 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 () возвращает строку, представляющую семейство ОС, на котором я работаю. Возвращает ноль, если ОС не поддерживается. На случай, если у меня возникнут проблемы с этим методом, я также опубликую его:

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-адреса, который я рефлексивно передаю загрузчику классов для загрузки библиотек. Моя проблема в том, что выполнение во время выполнения никогда не достигает load_libraries (), хотя это первый метод в main.

Одним из очевидных решений является статическая загрузка собственных библиотек с помощью пользовательских jar-файлов. Я не хочу статически связывать нативные библиотеки, если этого вообще можно избежать, потому что это противоречит цели переносимости платформы java. Другое решение, которое я уже вывел, - это исполняемый файл системы, который может запускать jar приложений с определенным автоматическим путем к классам из исполняемого файла системы, но, тем не менее, это решение требует нескольких исполняемых файлов, которые зависят от платформы.

Итак, вот мой вопрос:

Могу ли я заставить загрузчик классов запустить мой метод загрузки библиотеки, прежде чем он будет инициализировать статические объекты? Это потребует настроенного ClassLoader? Если для этого требуется пользовательский ClassLoader, решит ли проблема просто передача системного свойства, представляющего загрузчик классов по умолчанию?

Спасибо, надеюсь, я написал достаточно подробный вопрос!

1 Ответ

3 голосов
/ 27 марта 2011

Причина, по которой «загрузчик классов по умолчанию пытается загрузить статические классы задолго до того, как вызывается main», заключается в том, что у вашего основного класса есть статические переменные-члены и / или статический инициализатор.Когда загрузчик классов инициализирует ваш основной класс, он также должен инициализировать эти статические переменные.Для получения дополнительной информации см. VM Spec .

Решение - единственное решение good - состоит в устранении статической инициализации.Инициализируйте ваши статические переменные-члены внутри вашего main() или используйте для инициализации такую ​​среду, как Spring.

В целом, очень мало хороших вариантов использования статических переменных-членов.Большинство из них заканчивают тем, что стали хакерами для обеспечения доступа к ресурсам из произвольных точек программы.Это не только затрудняет тестирование программы, но и приводит к неожиданным цепочкам инициализации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...