Для чего люди используют класс, загружающийся?

Так, каждый учебник Java говорит о том, как гибкий Java - так как он может загрузить классы во время выполнения. Просто почините строку и дайте ее Class.forName(), и выгода ClassNotFoundException и обработайте его. Так для теории.

Можно ли дать примеры того, как Вы использовали класс Java, загружающийся для достижения функции, которая иначе не была бы возможна или легка? Обратите внимание, что я не спрашиваю, "какие большие вещи мы могли сделать?" - я ищу реальные примеры, быть этим приложение с открытым исходным кодом или - если можно описать, это, не выделяя слишком много детализирует - закрытое приложение.

Править: Несомненно, VM загружает классы лениво, поскольку ему нужны они. Это - закулисная вещь, пока я уверен, что все классы, в которых я буду когда-либо нуждаться, там. Как я обрабатываю a ClassNotFoundException? Предположим, что я записал ценность на десять страниц текста, и PrinterDriver класс не может быть найден.

16
задан Dan Rigby 2 May 2012 в 19:57
поделиться

18 ответов

Плагины - первое, что приходит на ум. Загрузка класса Java делает ее очень простой по сравнению с такими языками, как C++.

Один момент, о котором вы, возможно, не знаете, это то, что любая виртуальная машина Java сильно зависит от внутренней загрузки класса. Каждый раз при обращении, скажем, к методу интерпретатора байткода, он проверяет, не загружен ли уже класс, которому принадлежит метод, и если нет, то загружает его, используя тот же самый механизм, что и Class.forName(), перед тем как разрешить метод. Этот меканизм очень мощный, так как любое Java-приложение действительно действует как набор заменяемых компонентов, которые все динамически загружаются. Если ВМ хорошо написана, то она может, например, загружать классы через загрузчик пользовательских классов, который получает классы из сети, а не из обычных файлов.

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

10
ответ дан 30 November 2019 в 17:15
поделиться

Контейнеры сервлетов, такие как Tomcat, читают ваш файл конфигурации war/webapp из WEB-INF/web.xml и загружают подклассы сервлетов/фильтров/и т.д., основываясь на значениях строк, которые вы поместили в XML-файл. Для подключения к базе данных, они вытаскивают имя класса для загрузки из вашей конфигурации, например, "com.mysql.jdbc.Driver" для MySQL.

0
ответ дан 30 November 2019 в 17:15
поделиться

См. Поддержка Oracle , например, в весенней основе . Только потому, что каркас предлагает конкретную поддержку Oracle, вы, вероятно, не хотите развертывать источник данных Oracle как зависимость для вашего E.G. MySQL проекты. Поэтому вы заряжаете драйверы Oracle, реча драйверов в объем экземпляра обработчика Лоб.

-121--1732671-

Серверы приложений также сильно полагаются на классные загрузки , чтобы выделить другой развернутый модуль. Например.

  • Вы можете развернуть одно и то же веб-приложение дважды под другим путем
  • Два приложения могут зависеть от двух разных версий одной и той же библиотеки без конфликтов.

Благодаря магии классовых погрузчиков ...

5
ответ дан 30 November 2019 в 17:15
поделиться

Вот соответствующий фрагмент блога Сэма, в котором кратко рассказывается о политике кэширования.

http://blogs.msdn.com/samng/archive/2008/10/29/dynamic-in-c.aspx

DLR проверяет кэш на наличие данное действие уже привязано против текущего набора аргументов. В нашем примере мы бы сделали тип совпадение на основе 1, 2 и среды выполнения тип d. Если у нас попадание в кэш, затем мы возвращаем кэшированный результат. Если у нас нет попадания в кэш, то DLR проверяет, является ли приемник IDynamicObject. Эти парни по существу объекты, которые умеют заботиться о собственном обязательстве, например, как объекты COM IDispatch, реальные динамические объекты, такие как Ruby или Python, или некоторые объекты .NET, реализующие интерфейс IDynamicObject. Если это так любой из них, затем DLR выполняет вызов в IDO и просит его связать действия.

Обратите внимание, что результат вызова IDO для привязки - это дерево выражений, представляет результат привязки. Если это не IDO, то DLR вызовы в языковую привязку (в нашем случай, связующая среда выполнения C #) для связывания операция. Связующая среда выполнения C # свяжет действие и вернет дерево выражения, представляющее результат привязки. Один раз шаг 2 или 3 произошло, в результате дерево выражения объединено с механизм кэширования, чтобы любой последующие вызовы могут выполняться для кэш вместо быть отскока.

Однако Сэм не упоминает, что именно такая политика пропуска кэша. Существуют две основные политики пропуска кэша: (1) запуск промаха кэша при изменении типов аргументов, (2) запуск промаха кэша при изменении идентификаторов аргументов.

Очевидно, что первый гораздо более эффективен; отрабатывать, когда мы можем кэшировать только на основе типа, сложно. Детальная экзегеза того, как вся эта логика работает, займет довольно много времени; Надеюсь, я или Крис или Сэм сделаем запись в блоге на нем в один из этих дней.

-121--3186065-

Пользователи, регулярные выражения НЕЛЬЗЯ использовать для синтаксического анализа нерегулярных языков. Нерегулярные языки - это языки, для интерпретации которых требуется состояние (т.е. запоминание количества открытых скобок).

Все вышеперечисленные ответы потерпят неудачу на этой последовательности: «ABC (привет (мир) как ты)».

Прочитайте сообщение Джеффа Этвуда «Parsing Html The Cthulhu Way: https://blog.codinghorror.com/parsing-html-the-cthulhu-way/ », а затем используйте либо побочный синтаксический анализатор (циклический анализ символов в последовательности, проверьте, является ли символ скобкой или нет, поддерживайте стек), либо используйте lexer/parser, способный анализировать язык без контекста

Также смотрите эту статью википедии о «языке правильно соответствующих скобок:» https://en.wikipedia.org/wiki/Dyck_language

-121--973788-

Можно использовать метод Class:: forName, если класс находится в пути к классу. Однако если необходимо указать путь вместе с именем класса, например, c :\document\xyz.class, необходимо использовать класс URLClassLoader.

-2
ответ дан 30 November 2019 в 17:15
поделиться

Механизм classloader Java является мощным, поскольку предоставляет абстракционную точку именно в той точке, где загружен код, что позволяет выполнять следующие действия:

  • поиск битов класса в другом месте, кроме пути к классам (db, удаленный url, файловая система и т.д.)
  • загрузка исходного кода, который вы только что создали и скомпилировали самостоятельно (с помощью javac api)
  • загрузка байтового кода, который вы только что создали самостоятельно (скажем, с ASM)
  • загрузка кода и ИЗМЕНЕНИЕ его перед использованием (с ASM, Java-агентами и т.д.)
  • RE-код загрузки на лету
  • цепные погрузчики вместе в деревьях (обычное делегирование) или веб-страницы (стиль OSGi на основе родственных узлов) или все, что вы хотите

На точке изменения кода во время загрузки, есть мир интересных вещей, которые вы можете сделать, чтобы переделать ваш код - AOP, профилирование, трассировка, изменения поведения и т.д. В Terracotta мы полагались на абстракцию classloader, чтобы динамически загрузить класс, затем перехватить весь доступ к полям и динамически добавить возможность загрузки состояния из того же объекта на удаленном узле в кластере позже. Классные вещи.

-121--1732673-

Использование динамической загрузки класса также очень полезно для загрузки файлов конфигурации, как упоминает Тило. В более общем случае динамическая загрузка класса во многих ситуациях может создать хороший уровень абстракции файловой системы, упрощая настройку записи и чувствительный к конфигурации код. Просто убедитесь, что нужный ресурс находится на пути к классам, и загрузите его как InputStream.

Кроме того, с помощью пользовательского обработчика протокола в Java можно получить доступ к предметам на пути к классам по URL. Это не является преимуществом, характерным для динамической загрузки классов, но демонстрирует, как можно получить доступ к ресурсам пути к классам через тот же API, что и к другим ресурсам (даже удаленным). http://java.sun.com/developer/onlineTraining/protocolhandlers/

-121--1732668-

Любая рамка, основанный на конфигурации (стойки, jsf, пружина, спящий режим и т.д.), использует этот механизм. Любой продукт, основанный на архитектуре плагина, также использует эту функцию.

0
ответ дан 30 November 2019 в 17:15
поделиться

Использование динамической загрузки классов также очень полезно для загрузки файлов конфигурации, как упоминает Тило. В более общем плане, загрузка динамического класса может во многих ситуациях создать хороший уровень абстракции файловой системы, упрощая написание предпочтений и кода, чувствительного к конфигурации. Просто убедитесь, что нужный вам ресурс находится в пути к классам, и загрузите его как InputStream.

Кроме того, с помощью специального обработчика протокола в Java можно получить доступ к элементам в пути к классам через URL. Это не является преимуществом, характерным для динамической загрузки классов, но демонстрирует, как к ресурсам пути к классам можно получить доступ через тот же API, что и к другим ресурсам (даже удаленным). http://java.sun.com/developer/onlineTraining/protocolhandlers/

0
ответ дан 30 November 2019 в 17:15
поделиться

Реальный пример (как указано в вашем вопросе), проприетарное приложение (как явно разрешено вашим вопросом) ...

При запуске клиентское программное обеспечение связывается с нашим сервером (-ами) и говорит: «Реализация по умолчанию У меня есть панель интерфейса Foo (потому что на самом деле каждая версия 1.03, например, использует Foo), у вас есть лучший? " Если тем временем мы написали лучшую реализацию, мы ответим: «Ага, Bar старый, используйте Buz, это лучше».

Затем на стороне клиента используется загрузчик классов для загрузки последней реализации.

Это слишком упрощенно, но это реальный пример. Это не совсем похоже на упомянутый пример JRL: где устаревшие классы автоматически заменяются более новыми.

0
ответ дан 30 November 2019 в 17:15
поделиться

См. Поддержка обработки Oracle в пружинной структуре. Только потому, что каркас предлагает конкретную поддержку Oracle, вы, вероятно, не хотите развертывать источник данных Oracle как зависимость для вашего E.G. MySQL проекты. Поэтому вы заряжаете драйверы Oracle, реча драйверов в объем экземпляра обработчика Лоб.

0
ответ дан 30 November 2019 в 17:15
поделиться

Сообщения поступают из App Store, поэтому изменить текст невозможно. Ты должен пойти в другую сторону. Когда пользователь нажимает кнопку или ячейку таблицы, связанную с заблокированной функцией, отображается сообщение с просьбой купить ее. Если он/она подтвердит, появится сообщение App Store.

-121--4690782-

Python не имеет встроенного эквивалента sscanf , и в большинстве случаев имеет гораздо больше смысла анализировать входные данные, работая непосредственно с последовательностью, используя regexps или инструмент синтаксического анализа.

Возможно, в основном полезно для перевода C, люди внедрили sscanf , например, в этом модуле: http://hkn.eecs.berkeley.edu/~dyoo/python/scanf/

В данном конкретном случае, если вы просто хотите разделить данные на основе нескольких разделенных символов, re.split действительно является правильным инструментом.

-121--983425-

Я использую его при создании готового приложения, которое должно быть адаптировано мной или клиентом для удовлетворения конкретных требований клиента.

0
ответ дан 30 November 2019 в 17:15
поделиться

Я помню, как создавал загрузчик классов для удаленной загрузки классов. Приложение работало на одном узле, а классы хранились на другом узле.

А также, настроив загрузчик классов, вы можете преобразовывать классы по мере их загрузки. Это используется некоторыми фреймворками ORM, а также некоторыми фреймворками АОП.

2
ответ дан 30 November 2019 в 17:15
поделиться

Я думаю, что Junit может также использовать много функций отражения, чтобы сделать тестовые рамки универсальными.

1
ответ дан 30 November 2019 в 17:15
поделиться

Классический загрузчик также используется для неклассных ресурсов. Файлы конфигурации приходят на ум. Поскольку есть четко определенный порядок поиска, легко запускать свой собственный «log4j.xml» или "hibernate.properties", и приложение найдет и использует его.

2
ответ дан 30 November 2019 в 17:15
поделиться

Это может быть чрезвычайно полезно в ситуациях, когда вы используете API, а разработчики API фактически устарели некоторые классы из одной версии в другую (например, Contacts в Android).

Без рефлексии и динамической загрузки классов, основанной на имени строки, в данном случае было бы невозможно запустить одну и ту же программу на обеих версиях платформы, не получив класс, не найденный исключение во время выполнения. Но при этом одна и та же программа была немного подкорректирована и затем могла бы выполняться на обеих платформах.

2
ответ дан 30 November 2019 в 17:15
поделиться

Я уверен, что загрузка плагина в Java во многом зависит от этого.

Приложение проверяет именованные функции и выполняет их

Eclipse на самом деле использует это для плагинов

Ключевая идея заключается в выполнении кода, который не был запланирован во время разработки.

2
ответ дан 30 November 2019 в 17:15
поделиться

Ну, я использовал его для динамически загрузки драйверов JDBC в приложение J2EE. Weatheher Это могло быть сделано лучшим способом, я понятия не имею.

В то время было просто проще сделать forname () .

3
ответ дан 30 November 2019 в 17:15
поделиться

«Плагин» и это большое слово.

В основном вы можете загрузить класс, который вы не знаете, когда или не существует, когда вы пишете и компилируете свою программу.

Например, если вы хотите, чтобы программа выполнить проверку орфографии, вы можете написать интерфейс ScreenChecker , затем загрузите класс из файла конфигурации, который реализует интерфейс ScreenChecker . После этого вы можете написать любой соревнования и установить в файл конфигурации фактическое имя файла. Таким образом, ваша программа не должна знать, какой класс сделает проверку орфографии.

Драйвер БД, плагин Eclipse, язык скрипта, методы криптографии все делают таким образом, поскольку оригинальный писатель не знает (и в некоторых случаях не имеет идеи), какой класс на самом деле будет использоваться.

Надеюсь, это поможет.

5
ответ дан 30 November 2019 в 17:15
поделиться

Механизм Java classloader является мощным, так как он предоставляет точку абстракции именно в точке загрузки кода, что позволяет делать такие вещи:

  • нахождение битов класса где-то кроме classpath (db, remote url, файловая система и т.д.)
  • загрузка исходного кода, который вы только что создали и скомпилировали сами (с помощью javac api)
  • загрузка байтового кода, который вы только что сгенерировали сами (скажем, с помощью ASM)
  • загрузка кода и его модификация перед использованием (с помощью ASM, Java агентов), и т.д.)
  • RE-загрузка кода на лету
  • цепные загрузчики вместе в деревьях (обычное делегирование) или в сети (стиль OSGi на основе брата или сестры) или что угодно

С точки зрения модификации кода во время загрузки, есть целый мир интересных вещей, которые вы можете сделать для ремиксов вашего кода - AOP, профилирование, трассировка, модификация поведения и т.д. В Terracotta мы полагались на абстракцию classloader для динамической загрузки класса, затем перехватывали все обращения к полям и динамически добавляли возможность загрузки состояния с того же самого объекта на удаленном узле в кластере позже. Классная вещь.

1
ответ дан 30 November 2019 в 17:15
поделиться

JDBC API - отличный тому пример. Таким образом, вы можете настроить драйвер JDBC извне, например, в файле свойств:

driver = com.dbvendor.jdbc.Driver
url = jdbc:dbvendor://localhost/dbname
username = stackoverflow
password = youneverguess

.. который вы можете использовать как:

Properties properties = new Properties();
properties.load(Thread.currentThread().getResourceAsStream("jdbc.properties"));

String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
String username = properties.getProperty("username");
String password = properties.getProperty("password");

Class.forName(driver);
Connection connection = DriverManager.getConnection(url, username, password);

Каждая реализация драйвера JDBC в основном регистрируется в DriverManager внутри ] static блок инициализатора. Это именно тот, который выполняется во время Class # forName () .

package com.dbvendor.jdbc;

public class Driver implements java.sql.Driver {

    static {
         java.sql.DriverManager.registerDriver(new Driver());
    }

    private Driver() {
        // ...
    }

    public boolean acceptsURL(String url) {
        return url.startsWith("jdbc:dbvendor");
    }

}

Поскольку DriverManager примерно выглядит так (на самом деле он использует старомодный Vector )

private static final Set<Driver> drivers = new HashSet<Driver>();

public static void registerDriver(Driver driver) {
    drivers.add(driver);
}

public static Connection getConnection(String url, String username, String password) throws SQLException {
    for (Driver driver : drivers) {
        if (driver.acceptsURL(url)) {
            return driver.connect(url, username, password);
        }
    }
    throw new SQLException("No suitable driver");
}

... вы можете получить от него соединение без необходимости создать экземпляр самого драйвера!

Таким образом, код JDBC становится легко переносимым. Вы можете изменить БД или распространить код среди пользователей с разными БД без необходимости изменять / взламывать / перестраивать сам код.

Этот подход использует не только JDBC, но и другие API, такие как Servlet API, ORM, такие как Hibernate / JPA, фреймворки внедрения зависимостей и т.д. . Все это делает код более портативным и легко подключаемым.

2
ответ дан 30 November 2019 в 17:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: