Процесс загрузки класса
Основная задача загрузчика класса - загружать файлы класса в JVM. Как показано на рисунке ниже, процесс делится на три этапа:
1. Загрузка: Найдите файл класса для загрузки, и загрузите его байтовый поток в JVM;
2. Ссылка: распределите самую основную структуру памяти для загрузки для сохранения ее информации, такой как свойства, методы и ссылочные классы. На этом этапе класс все еще недоступен;
(1) проверка: проверьте загруженный байтовый поток, такой как формат и безопасность;
(2) распределение памяти: подготовьте пространство памяти для этого класса, чтобы представлять его свойства, методы и ссылочные классы;
(3) Анализ: загрузите другие классы, на которые ссылаются этот класс, такие как родительский класс, реализованные интерфейсы и т. Д.
3. Инициализация: назначить значения переменным класса.
Уровень загрузчика класса
Пунктирная линия на рисунке ниже представлена несколько важных классовых погрузчиков, предоставленных JDK, и подробное описание выглядит следующим образом:
(1) Загрузчик класса начальной загрузки: при запуске класса, содержащей основную функцию, загрузите пакет JAR в указанный каталог JAVA_HOME/LIB или -xbootclasspath;
(2) Загрузка класса расширения: загрузите пакет JAR в каталог java_home/lib/ext или -djava.ext.dirs.
(3) Загрузчик системы Class: Load ClassPath или -djava.class.path Class или пакет JAR в указанном каталоге.
Вам нужно знать:
1. В дополнение к загрузчику класса начальной загрузки, другие загрузчики класса являются подклассами класса Java.lang.classloader;
2. Загрузчик начальной загрузки не реализован в Java. Если вы не используете персонализированный загрузчик класса, то java.lang.string.class.getClassloader () является нулевым, а родительский загрузчик класса расширения также является нулевым;
3. Несколько способов получить классовые погрузчики:
(1) Получение загрузчика класса начальной загрузки: то, что вы получаете, должно быть нулевым при попытке получить загрузчик класса Bootstrap. Вы можете проверить его следующим образом: используйте метод getClassLoader объектов класса в пакете RT.JAR, такой как java.lang.string.class.getClassloader (), чтобы получить или получить загрузчик класса расширения, а затем вызовать метод GetParent, чтобы получить;
(2) Получить загрузчик класса расширения: используйте метод getClassloader объекта класса в пакете JAR в каталоге Java_Home/Lib/EXT или сначала получите загрузчик класса системного класса, а затем получить его с помощью метода GetParent;
(3) Получить загрузчик системного класса: вызовите метод getClassLoader объекта класса, содержащий основную функцию, или поток вызова. CurrentThread (). GetContextClassLoader () или Call ClassLoader.getSystemClassLoader () в основной функции;
(4) Получить пользовательский загрузчик класса: вызовите метод GetClassLoader объекта класса или поток вызова
Операционные принципы классового загрузчика
1. Принцип агента
2. Принцип видимости
3. Принцип уникальности
4. Proxy Principle Принцип прокси относится к загрузке класса, с просьбой загружать его родительский погрузчик при загрузке класса, а родительский загрузчик также запросит свой родительский прокси -погрузчик для загрузки, как показано на рисунке ниже.
Зачем использовать прокси -режим? Прежде всего, это может уменьшить дублиручную загрузку класса. (Есть ли другая причина?)
Где неправильно понять:
Как правило, считается, что прокси -порядок загрузчика класса является родителем, то есть: есть:
1. При загрузке класса загрузчик класса сначала проверяет, загрузил ли он класс. Если он был загружен, он вернется; В противном случае, попросите родительского погрузчика до прокси;
2. родительский погрузчик повторяет работу 1 до загрузчика класса начальной загрузки;
3. Если загрузчик класса начальной загрузки не загружает класс, он попытается загрузить его, и если он будет успешным, он вернется; Если это не удастся, выброшен ClassNotFoundException, оно будет загружено дочерним погрузчиком;
4. Погрузчик подкласса выявляет исключение и пытается загрузить его. Если это удастся, он возвращается. Если он не удается, он бросает ClassNotFoundException до тех пор, пока не будет начат загруженный подклассный погрузчик.
Это понимание является правильным для погрузчиков, таких как загрузчик класса начальной загрузки, погрузчик класса расширения и загрузчик системного класса, но некоторые персонализированные погрузчики не соответствуют. Например, некоторые загрузчики классов, реализованные IBM Web Sphere Server, являются родителями последними, что является дочерним погрузчиком, который пытается сначала загрузить. Если нагрузка не удается, родительский погрузчик будет спрошен. Причиной этого является: если вы ожидаете, что определенная версия Log4J будет использоваться всеми приложениями, поместите его в библиотеку was_home, и она будет загружена, когда начался. Если приложение хочет использовать другую версию log4j, его нельзя достигнуть, если он сначала использует родитель, потому что класс в log4j уже загружен в родительский загрузчик. Однако, если родители последнее используется, загрузчик класса, ответственный за загрузку приложения, будет определять приоритет загрузке другой версии log4j.
Принцип видимости
Видимость каждого класса для загрузчика класса отличается, как показано на рисунке ниже.
Чтобы расширить знания, Osgi использует эту функцию. Каждый пакет загружается отдельным загрузчиком класса, поэтому каждый загрузчик класса может загружать версию определенного класса, поэтому вся система может использовать несколько версий класса.
Принцип уникальности
Каждый класс загружается максимум один раз в загрузчик.
Расширенные знания 1: Точнее, синглтонский шаблон относится к объекту Singleton, который только одна копия класса в наборе загрузчиков класса.
Расширенные знания 2: класс может быть загружен несколькими погрузчиками класса. Каждый объект класса находится в своем собственном пространстве имен. При сравнении объекта класса или типа преобразование экземпляра он будет сравнивать соответствующее пространство имен одновременно, например:
Класс Klass загружается ClassLoadera, предполагая, что объектом класса является klassa; Он загружается ClassLoaderB, предполагая, что объект класса является KlassB, тогда klassa не равна KlassB. В то же время, когда экземпляр Classa будет отменен в KlassB, будет брошено исключение ClassCastException.
Зачем вам персонализированный загрузчик класса? Персонализированный загрузчик класса добавляет большую гибкость к языку Java. Основным использованием:
1. Классы могут быть загружены из нескольких мест, например, в сети, в базе данных или даже мгновенно составленные исходные файлы;
2. После персонализации класс загружатель может загрузить определенную версию файла класса в принципе во время выполнения;
3. После персонализации класс загрузчик может динамически разгрузить некоторые классы;
4. После персонализации класс -загрузчик может расшифровать и распаковать класс перед загрузкой класса.
Неявная и явная загрузка классов
Неявная загрузка: когда на класс ссылается, унаследован или экземпляр, он будет неявно загружен. Если нагрузка не удается, выброшен NoclassDeffounderror.
Явная загрузка: используйте следующий метод, если нагрузка не удается, будет выброшена класснотфундэкспрессия.
cl.loadclass (), cl является экземпляром загрузчика класса;
Class.forname (), загружается с использованием загрузчика класса текущего класса.
Если выполнение статического блока класса имеет следующие классы:
Пакет Cn.fengd; public class Dummy {static {System.out.println ("hi"); }} Создайте еще один тестовый класс:
Пакет Cn.fengd; public class classloadertest {public static void main (string [] args) бросает instantiationException, exception {try { / * * Различные способы загрузки. */ Class c = classloadertest.class.getClassloader (). } catch (Exception e) {// todo автоматически сгенерированный блок e.printstacktrace (); }}}Насколько это эффективно после бега?
Что, если он заменит Class.forname ("cn.fengd.dummy"); Или новое манекен ()?
Привет будет выводом.
Часто задаваемые вопросы:
1. Загружен ли указанный тип различным загрузчикам класса по -прежнему одинаковый тип?
В Java класс использует свое полностью квалифицированное имя класса в качестве идентификатора. Точное имя класса совпадения, упомянутое здесь, включает имя пакета и имя класса. Однако в JVM класс использует свое полное имя и экземпляр загрузчика класса класса загрузки в качестве уникальных идентификаторов. Классы, загруженные различными погрузчиками класса, будут размещены в разных пространствах имен. Мы можем использовать два пользовательских загрузчика класса для загрузки пользовательского типа (обратите внимание, что не размещайте байт -код пользовательского типа в путь системы или путь к расширению, в противном случае он будет загружен сначала погрузчиком класса системного класса или погрузчика класса расширения), а затем использовать два экземпляра класса, полученные, чтобы сделать Java.Lang.Object.Equals (…), и вы получите неравные результаты. Это можно сделать, написав два пользовательских загрузчика класса для загрузки одного и того же пользовательского типа, а затем вынести суждение; В то же время вы можете проверить загрузку Java.* Тип, а затем сравнить и проверить результаты испытаний.
2. Прямо вызовите метод class.forname (name string) в коде. Какой класс загрузчик запустит поведение загрузки класса?
Class.forname (String name) будет использовать загрузчик класса, который по умолчанию вызывает класс для загрузки класса. Давайте напрямую проанализируем соответствующий код JDK:
//java.lang.class.java publicstatic class <?> forname (String classname) throws classnotfoundexception {return forname0 (classname, true, classloader.getcallerclassloader ());} // java.lang.classloader.java// возвращает класс Invoker Class Loader, или Null I.StaticLoader. getCallerclassloader () {// Получить тип Calls Class (Caller) Cllass Caller = Reflection.getCallerclass (3); // это может быть нулевым, если виртуальная машина запрашивает его, если (caller == null) {returnnull; } // Вызовите локальный метод в java.lang.class, чтобы получить загрузку класса, который загружает Calling Class (Caller) return Caller.getClassloader0 ();} // java.lang.class.java//native реализации виртуальной машины, чтобы получить загрузку класса текущего класса Nativeloadler getClassLoader0 (); 3. При написании пользовательского загрузчика класса, если родительский загрузчик не установлен, то что такое родительский погрузчик?
Когда родительский загрузчик класса не указан, загрузчик системы Class используется по умолчанию. Некоторые люди могут не понимать, теперь давайте посмотрим на соответствующую реализацию кода JDK. Как мы все знаем, мы пишем пользовательский загрузчик класса прямо или косвенно наследуя от абстрактного класса java.lang.langloader. Соответствующий конструктор по умолчанию без параметров реализуется следующим образом:
// Выдержка из java.lang.classloader.javaprotected classloader () {SecurityManager Security = System.getSecurityManager (); if (security! = null) {security.checkcreateClassloader (); } this.parent = getSystemClassloader (); инициализирован = true;}Давайте посмотрим на соответствующую реализацию метода getSystemClassLoader ():
privateStaticsynchronizedvoid initsystemclassloader () {// ... sun.misc.launcher l = sun.misc.launcher.getlauncher (); scl = l.getclassloader (); // ...}Мы можем написать простой тестовый код, чтобы проверить его:
System.out.println (sun.misc.launcher.getLauncher (). GetClassLoader ());
Соответствующий выход этой машины выглядит следующим образом:
sun.misc.launcher$appclassloader@197d257
Таким образом, теперь мы можем поверить, что когда пользовательский загрузчик класса не указывает загрузчик родительского класса, родительский загрузчик класса по умолчанию является загрузчиком системного класса. В то же время мы можем сделать следующие выводы:
Мгновенный пользовательский загрузчик класса не указывает на загрузчик родительского класса, а затем классы в следующих трех местах также могут быть загружены:
(1) Занятия в рамках <Java_Runtime_home>/lib
(2) классы в месте, указанном системной переменной java.ext.dir
(3) Классы по текущему пути класса проекта или в месте, указанном системной переменной java.class.path
4. При написании пользовательского загрузчика класса, что произойдет, если погрузчик родительского класса будет вынужден быть нулевым? Если пользовательский загрузчик класса не может загрузить указанный класс, он определенно не удастся?
Спецификация JVM предусматривает, что, если пользовательский загрузчик класса заставит родительский загрузчик класса NULL, загрузчик класса запуска будет автоматически установлен на загрузчик родительского класса текущего пользовательского загрузчика класса (эта проблема была проанализирована ранее). В то же время мы можем сделать следующие выводы:
Если мгновенный пользовательский загрузчик класса не указывает на загрузчик родительского класса, то он также может загрузить класс в рамках <java_runtime_home>/lib, но класс в рамках <java_runtime_home>/lib/ext Directory не может быть загружен в это время.
ПРИМЕЧАНИЕ. Предполагаемые выводы вопросов 3 и 4 основаны на самого пользовательского загрузчика класса, который продолжает логику делегирования по умолчанию java.lang.classloader.loadclass (…). Если пользователь изменяет эту логику делегирования по умолчанию, приведенный выше предполагаемый вывод может быть недопустимым. См. Вопрос 5 для деталей.
5. Каковы общие моменты внимания при написании пользовательских загрузчиков классов?
(1) Как правило, старайтесь не переопределять логику делегирования в существующем методе LoadClass (…). Как правило, это делается в версиях до JDK 1.2, и выясняется, что это, скорее всего, приведет к тому, что загрузчик класса по умолчанию системы не работает должным образом. В спецификации JVM и документации JDK (1,2 или более поздних версий) не рекомендуется, чтобы пользователи перезаписывали метод LoadClass (…). Напротив, разработчикам явно напоминают, чтобы перезаписать логику Findclass (…) при разработке пользовательского загрузчика класса. Возьмите пример, чтобы проверить проблему:
// Пользователь пользовательский класс загрузчик неверно загрузчик. } Защищенный класс <?> FindClass (String name) Throws ClassNotFoundException {// Предположим, здесь только конкретный каталог, отличный от библиотеки проекта D: /для загрузки конкретного кода реализации класса}}}}}}}}}}}}}}}}}Благодаря предыдущему анализу мы уже знаем, что по умолчанию пользовательского загрузчика класса (неверно загрузчик).
Распознанный загрузчик класса является загрузчиком системного класса, но выводы четырех типов проблем сейчас недопустимы. Каждый может
Просто проверьте это, теперь <java_runtime_home>/lib, <java_runtime_home>/lib/ext and
Классы на пути класса программы не могут быть загружены.
Вопрос 5 Тестовый код 1
publicclass fronwclassloadertest {publicstaticvoid main (string [] args) {try {fronkclassloader loader = new FronkClassloader (); Class ClassLaded = Loader.LoadClass ("beans.account"); System.out.println (classulad.getName ()); System.out.println (classulad.getClassLoader ()); } catch (Exception e) {e.printstackTrace (); }}}(ПРИМЕЧАНИЕ: D: «Классы" бобы ". Класс физически существует)
Результат вывода:
java.io.filenotfoundexception: D: «классы» java «lang» object.class (система не может найти указанный путь.) на java.io.fileinputstream.open (нативный метод) на java.io.fileinputstream. <init> (fileinputeream.java:106) atclasclasslair.findclasclass.findclassclass. at fruckclassloader.loadclass (fronkclassloader.java:29) на java.lang.classloader.loadclassinternal (classloader.java:319) на java.lang.classloader.defineclass1 (нативный метод) на java.lang.classload.defineclass (classulder. java.lang.classloader.defineclass (classloader.java:400) на fronkclassloader.findclass (fronkclassloader.java:43) на fronwclassloader.loadclass (fronkclassloader.java:29) на unforclaslestest.main (неверно загрузка. java.lang.noclassdeffounderror: java/lang/obj FrupsClassler.findClass (FronkClassLoader.java:43) на frupslasler.loadclass (fronkclassloader.java:29) на frucklasseltest.main (неверный closlestestest.java:27)
Это означает, что даже супертип java.lang.object от загруженного типа не может быть загружен. Логические ошибки, перечисленные здесь, вызванные перезапись CloadClass (…), очевидно, относительно просты, а логические ошибки, вызванные фактически, могут быть гораздо более сложными.
Вопрос 5 Тест 2
// пользователь пользовательский пользовательский класс загрузчик неверный classloader.java (не перезаписать логику LoadClass) publicClassWrongClassLextErxEndEnds ClassLoader {Protected Class <?> FindClass (String name) Throws ClassNotFoundException {// Предположим, что это просто конкретный каталог, отличный от проекта D: /Библиотека для загрузки Class Code omated}}}}}}}После изменения пользовательского кода загрузчика класса FrontClassLoader.java запустите тестовый код, и результат вывода выглядит следующим образом:
beans.accountwrongclassloader@1c78e57
Это показывает, что Beans.account успешно загружен и загружается неверно загрузчиком класса.
Я не думаю, что есть необходимость объяснить причины этого, и вы сможете проанализировать это.
(2) Правильно настройте загрузчик родительского класса посредством анализа вопроса 4 и вопроса 5 выше. Я лично думаю, что это самый важный момент при настройке загрузчиков пользовательских классов, но его часто игнорируют или легко приводится. С учетом анализа предыдущего кода JDK в качестве основы, я думаю, что каждый может привести примеры сейчас.
(3) Убедитесь, что логическая правильность метода FindClass (String) попробуйте точно понять задачи загрузки, которые должны быть выполнены загрузчиком класса для определения, и убедитесь, что соответствующее содержимое байт -кода может быть получено в наибольшей степени.
6. Как определить, какие пути может загружать загрузчик класса системного класса?
Во -первых, вы можете напрямую вызовать classloader.getSystemClassloader () или другие методы для получения загрузчика системного класса (загрузчик системного класса и загрузчик класса расширения сами получен из UrlClassLoader), и вы можете получить его, вызывая метод getUrls () в UrlClassLoader;
Во -вторых, вы можете напрямую просмотреть информацию о записи текущей дороги класса, получив атрибут системы java.class.path. System.getProperty ("java.class.path")
7. Как определить, какие пути может загружать стандартный погрузчик класса удлинения?
Метод первый:
try {url [] exturls = ((urlclassloader) classloader.getSystemClassloader (). getParent ()). getUrls (); for (int i = 0; i <exturls.length; i ++) {System.out.println (exturls [i]); }} catch (Exception e) {//…}Соответствующий выход этой машины выглядит следующим образом:
Файл:/d: /demo/jdk1.5.0_09/jre/lib/ext/dnsns.jarfile:/d: /demo/jdk1.5.0_09/jre/lib/ext/localedata.jarfile :/D: /demo/jdk1.5.0_09/jre/lib/ext/sunjce_provider.jarfile:/d: /demo/jdk1.5.0_09/jre/lib/ext/sunpkcs11.jar