Для чего люди используют загрузку классов? - PullRequest
16 голосов
/ 01 февраля 2010

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

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

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

Ответы [ 18 ]

10 голосов
/ 01 февраля 2010

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

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

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

5 голосов
/ 01 февраля 2010

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

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

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

5 голосов
/ 01 февраля 2010

«ПЛАГИН», и это большое слово.

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

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

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

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

3 голосов
/ 01 февраля 2010

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

В то время было просто сделать звонок forName().

2 голосов
/ 02 февраля 2010

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, структуры внедрения зависимостей, и так далее, используют отражение для загрузки классов на основе внешне настраиваемых файлов свойств, файлов конфигурации XML и / или аннотаций.Все это делает код намного более переносимым и подключаемым.

2 голосов
/ 01 февраля 2010

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

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

2 голосов
/ 01 февраля 2010

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

2 голосов
/ 01 февраля 2010

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

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

2 голосов
/ 01 февраля 2010

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

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

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

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

1 голос
/ 02 февраля 2010

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

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

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

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