При разработке Java основные знания, с которыми вы должны быть знакомы в механизме загрузчика класса. Эта статья кратко суммирует механизм загрузчика Java. Поскольку разные реализации JVM различны, контент, описанный в этой статье, ограничен Hotspot JVM.
Эта статья начнется с четырех аспектов ClassLoader, родительской модели делегирования, предоставленной JDK, как настроить класс загрузчика и сценарии, которые нарушают механизм родительского делегирования в Java.
JDK Default ClassLoader
JDK предоставляет следующие загрузчики класса по умолчанию
Bootstrp Loader
Загрузчик BootStrp написан на языке C ++. Он инициализируется после запуска виртуальной машины Java. В основном он отвечает за загрузку пути, указанную в %java_home %/jre/lib, параметр -xbootclasspath и классы в %java_home %/jre/классы.
Extclassloader
Загрузчик BootStrp загружает extclassloader и устанавливает родительский загрузчик extclassloader на загрузку BootStrp.extclassloader в Java, в частности, Sun.misc.launcher $ extclassloader. Extclassloader в основном загружает %java_home %/jre/lib/ext, все каталоги классов в соответствии с этим путем и библиотеки классов в пути, указанной системной переменной java.ext.dirs.
Appclassloader
После загрузки загрузчика BootStrp загружает extclassloader, загрузчик Appclassloader будет загружен, а родительский загрузчик Appclassloader указан как ExtClassLoader. Appclassloader также написан на Java. Его класс реализации - sun.misc.launcher $ appclassloader. Кроме того, мы знаем, что в классе есть метод загрузчика GetSystemClassloger. Этот метод возвращает Appclassloader.appclassloader в основном отвечает за загрузку документа класса или JAR в месте, указанном ClassPath. Это также загружатель класса по умолчанию для Java -программ.
Родительская модель делегации
Загрузка класса загрузчика в Java принимает родительский механизм делегата. При загрузке классов с использованием родительского механизма делегата принимаются следующие шаги:
В настоящее время ClassLoader впервые проверяет, был ли этот класс загружен из класса, который он уже загрузил. Если он был загружен, он прямо вернет оригинальный класс.
Каждый загрузчик класса имеет свой собственный кеш загрузки. Когда класс загружен, он будет помещен в кэш и может быть возвращен непосредственно, когда он будет загружен в следующий раз.
Когда кеш класса загрузчика не найден, загружатель родительского класса делегируется для загрузки. Погрузчик родительского класса принимает ту же стратегию. Сначала проверьте его собственный кеш, а затем делегируйте родительский класс родительского класса для загрузки, вплоть до загрузки Boottrp ClassLoader.
Когда все родительские загрузки класса не загружаются, они загружаются текущим загрузчиком класса и помещают их в свой собственный кэш, чтобы их можно было вернуть непосредственно в следующий раз, когда будет запрос на загрузку.
Говоря об этом, вы можете задаться вопросом, почему Java принимает такой механизм делегирования? Чтобы понять эту проблему, мы представляем еще одну концепцию «пространство имен» о ClassLoader, что означает, что для определения определенного класса вам нужно полностью квалифицированное имя класса и загрузить этот класс класса, чтобы совместно определить его. То есть, даже если полностью квалифицированные имена двух классов одинаковы, потому что разные загрузчики класса загружают этот класс, то это другой класс в JVM. Понимая пространство имен, давайте посмотрим на модель делегатов. После принятия модели делегата интерактивные возможности различных загрузчиков увеличиваются. Например, как упомянуто выше, библиотеки классов, предоставленные нашим JDK Binsheng, таким как Hashmap, LinkedList и т. Д. После того, как эти классы загружаются загрузчиком класса BootStrp, независимо от того, сколько загрузчиков классов в вашей программе могут быть обмены, что позволяет избежать путаницы, вызванной различными загрузчиками класса, загружающих разные классы одного имени.
Как настроить ClassLoader
В дополнение к загрузке ClassLoader, указанному выше, Java также позволяет приложениям настраивать загрузчик ClassLoader. Если вы хотите настроить загрузку класса, нам нужно реализовать его, унаследовав java.lang.classloader. Далее, давайте посмотрим на несколько важных методов, на которые нам нужно обратить внимание при настройке загрузчика класса:
1. Метод загрузки
Метод Loadclass объявляет
открытый класс <?> LoadClass (String name) Throws ClassNotFoundException
Выше приведено прототип объявления метода нагрузки. Реализация механизма родительского делегирования, упомянутого выше, фактически реализована в этом методе. Давайте посмотрим на код этого метода, чтобы увидеть, как он реализует родительскую делегирование.
Метод LoadClass реализовать
public class <?> LoadClass (String name) Throws classNotFoundException {return LoadClass (name, false);}Из вышеперечисленного мы видим, что метод LoadClass вызывает метод LoadCclass (имя, false), поэтому давайте посмотрим на реализацию другого метода LoadClass.
Class LoadClass (название строки, логическое решение)
Защищенный синхронизированный класс <?> LoadClass (имя строки, логическое разрешение) Throws ClassNotFoundException {// сначала проверьте, был ли класс уже загружен класс C = findLoadedClass (name); // Проверьте, был ли класс загружен, если (c == null) {try {if parent! = null) {c = parent.ladclass (name, faly inte hips it it hips it hip hip hip hip hip hip hipe int it hy int it hy int it hy int it i int hiped); Погрузчик указан, родительский погрузчик делегируется для загрузки. } else {c = findbootStrapClass0 (name); // Если нет родительского загрузчика класса, то делегируйте загрузчик Bootstrap для загрузки}} catch (classnotfoundexception e) {// Если все еще не найдено, то вызовите FindClass в порядке // найти класс. c = findclass (имя); // Если загрузка родительского класса не загружена, она будет загружена через свой собственный класс Finds. }} if (Resolve) {ResolVeclass (c);} return c;}В приведенном выше коде я добавил комментарии, чтобы четко увидеть, как работает механизм родительского делегирования класса Load. Одна вещь, которую мы должны отметить здесь, это то, что открытый класс <?> LoadClass (String name) Chos ClassNotFoundException не помечается как окончательный, что означает, что мы можем переопределить этот метод, что означает, что родительский механизм делегирования может быть нарушен. Кроме того, мы заметили, что есть метод Findclass выше. Далее, давайте поговорим о том, плохим ли этот метод.
2.findclass
Мы проверяем исходный код java.lang.classloader и обнаруживаем, что реализация Findclass выглядит следующим образом:
Защищенный класс <?> FindClass (String name) Throws ClassNotFoundException {Throw New ClassNotFoundException (name);}Мы видим, что реализация этого метода по умолчанию состоит в том, чтобы напрямую бросить исключения, но на самом деле этот метод оставляется наше приложение для переопределения. Конкретная реализация зависит от вашей логики реализации. Вы можете читать с диска или получить поток байтов из файлов класса из сети. После получения двоичного класса вы можете передать его на Decipleclass для дальнейшей загрузки. Давайте опишем DefineClass позже. Хорошо, через приведенный выше анализ мы можем сделать следующие выводы:
Когда мы пишем нашего собственного загрузчика класса, если мы хотим следовать механизму родительского делегирования, нам нужно только переопределить Findclass.
3. decianclass
Давайте сначала посмотрим на исходный код deciplass:
DefineClass
Защищенный окончательный класс <?> decipleclass (имя строки, byte [] b, int off, int len) бросает classformaterror {return decileclass (name, b, off, len, null);}Из приведенного выше кода мы видим, что этот метод определяется как окончательный, что означает, что этот метод не может быть переопределен. На самом деле, это также единственная запись, оставленная нам JVM. Благодаря этой уникальной записи JVM гарантирует, что файл класса должен соответствовать определению класса, указанного в спецификации виртуальной машины Java. Этот метод, наконец, будет вызвать нативный метод для реализации загрузки реального класса.
Хорошо, через описание выше, давайте подумаем о следующем вопросе:
Если мы сами написали класс java.lang.string, можем ли мы заменить класс, который называет сам JDK?
Ответ нет. Мы не можем этого достичь. Почему? Я вижу много онлайн -объяснений о том, что родительский механизм делегирования решает эту проблему, но на самом деле это не очень точно. Поскольку родительский механизм делегирования может быть сломан, вы можете написать загрузчик класса для загрузки класса Java.lang.String, который вы написали, но вы обнаружите, что он не будет успешно загружаться. В частности, для классов, начиная с Java.*, Реализация JVM гарантировала, что она должна быть загружена BootStrp.
Сценарии, которые не следуют «механизму делегирования родителей»
Вышеупомянутое, что родительский механизм делегирования состоит в том, чтобы в основном реализовать проблему взаимодействия классов, загруженных между различными загрузчиками. Классы, которые разделяют все, передаются родительскому загрузке для загрузки, но в Java действительно существует ситуация, где классы, загруженные погрузчиком родительского класса, должны использовать классы, загруженные дочерним погрузчиком. Давайте поговорим о возникновении этой ситуации.
В Java существует стандарт SPI (ServiceProviderInterface), в котором используются библиотеки SPI, такие как JDBC, JNDI и т. Д. Мы все знаем, что JDBC требуют водителей, предоставленных третьими лицами, а пакет JAR водителя помещается в класс нашего приложения. Сам API JDBC является частью JDK, предоставленного JDK, и он был загружен BootStrp. Итак, как мне загрузить классы реализации, предоставленные сторонними производителями? Java вводит концепцию загрузки контекста потока. Загрузчик класса потока будет наследовать от родительского потока по умолчанию. Если это не указано, по умолчанию является загрузчик системного класса (AppClassLoader). Таким образом, когда загружается сторонний драйвер, его можно загрузить через загрузчик класса потока потока.
Кроме того, для реализации более гибкого загрузчика класса OSGI и некоторых JavaAppservers он также нарушает механизм родительского делегирования.
Суммировать
Выше приведено все содержание этой статьи об анализе кода использования и использования механизма загрузчика Java. Я надеюсь, что это будет полезно для всех. Заинтересованные друзья могут продолжать ссылаться на другие связанные темы на этом сайте. Если есть какие -либо недостатки, пожалуйста, оставьте сообщение, чтобы указать это. Спасибо, друзья, за вашу поддержку на этом сайте!