Запутать
В прошлом, если смотреть на исходный код, я всегда встречал код в структуре с помощью Thread.currentThread.getContextClassloader (), чтобы получить загрузчик класса контекста текущего потока и использовать этот загрузчик класса контекста для загрузки класса.
Когда мы обычно пишем код в программе, когда мы хотим динамически загружать классы, мы обычно используем class.forname () для загрузки необходимых классов. Например, наиболее распространенным является то, что когда мы программируем JDBC, мы используем class.forname () для загрузки драйвера JDBC.
try {return class.forname ("oracle.jdbc.driver.oracledriver");} catch (classnotfoundexception e) {// skip}Так почему же, когда мы используем class.forname () для загрузки класса, если класс не может быть найден, мы все равно пытаемся загрузить класс, используя загрузчик класса, полученный Thread.currentThread.getContextLoader ()? Например, мы можем встретить следующий код:
try {return class.forname (classname);} catch (classnotfoundexception e) {// skip} classloader ctxclassloader = thread.currentThread (). getContextClassloader (); if (ctxclassloader! = null) {try {clazz = ctxclassloader.loadclass (classname); } catch (classnotfoundexception e) {// skip}}Жирная часть здесь заключается в использовании загрузчика, полученного Thread.currentThread.getContextLoader () для загрузки класса. Очевидно, что загрузчик класса, используемый при загрузке class.forname (), может отличаться от загрузчика класса, полученного Thread.currentThread.getContextLoader (). Так почему же разница?
Java Class Loader
Прежде чем понять, почему используется этот загрузчик класса, полученный с помощью Thread.currentThread.getContextLoader (), давайте сначала поймем загрузчик класса (ClassLoader), используемый в JVM.
По умолчанию в JVM есть три типа загрузчиков класса:
Загрузчик начальной загрузки
Загрузчик класса загрузчика класса Bootstrap - это загрузчик класса, встроенный в JDK, который используется для загрузки классов внутри JDK. Загрузчик класса начальной загрузки используется для загрузки классов ниже $ Java_home/jre/lib в JDK, таких как классы в пакете Rt.jar. Загрузчик начальной загрузки является частью JVM и обычно написан в собственном коде.
Загрузчик класса расширения
Загрузчик класса Extension Class Loader в основном используется для загрузки классов в пакете расширения JDK. Как правило, пакеты под $ java_home/lib/ext загружаются через этот загрузчик класса, и классы в рамках этого пакета в основном начинаются с Javax.
Системный класс погрузчик
Loader Class Class Class Class также называется загрузчиком класса приложения (AppClassLoader). Как следует из названия, этот загрузчик класса используется для загрузки кода приложения, который обычно пишут разработчики. Погрузчик системы Class используется для загрузки классов на уровне приложения, хранящихся в пути ClassPath.
В следующем коде перечислены эти три загрузки класса:
открытый класс Mainclass {public static void main (string [] args) {system.out.println (integer.class.getClassloader ()); System.out.println (logging.class.getClassloader ()); System.out.println (mainclass.class.getClassloader ()); }}Где получить загрузчик Bootstrap Class Loader возвращает нулевое значение навсегда
null # bootstrap class Loader sun.misc.launcherDextClassLoader@5e2de80c # endension class loader sun.misc.launcher$appclassloader@18b4aac2 # Системный класс загрузчик загрузки
Родительская модель делегации
Введенные выше погрузчики с тремя типами не изолированы, они имеют иерархические отношения:
Три класса погрузчики работают вместе через эти иерархические отношения и несут ответственность за загрузку классов вместе. Вышеуказанная иерархическая модель называется моделью «родительской делегации» классового загрузчика. Родительская модель делегирования требует, чтобы все загрузчики класса имели родительский загрузчик, за исключением загрузчика класса начальной загрузки верхнего уровня. Когда загрузчик класса загружает класс, сначала проверьте, есть ли классы в кэше, которые были загружены. Если нет, то родительский погрузчик делегируется для загрузки класса сначала. Родительский погрузчик выполняет ту же работу, что и предыдущий дочерний погрузчик, пока запрос не достигнет загрузчика класса начальной загрузки верхнего уровня. Если родительский погрузчик не может загрузить требуемый класс, то дочерний погрузчик попытается загрузить класс сам. Это работает аналогично следующему:
Мы можем использовать код в ClassLoader в JDK, чтобы увидеть реализацию механизма родительского делегирования. Код реализован в classloader.loadclass ()
Поверенный класс <?> LoadClass (String name, Boolean Resivel) Throws ClassNotFoundException Synchronized (getClassLoadingLock (name)) {// сначала проверьте, был ли класс уже загружен Class <?> C = findLoadedClass (name); if (c == null) {long t0 = System.nanotime (); попробуйте {if (parent! = null) {c = parent.loadclass (имя, false); } else {c = findbootStrapClassornull (name); }} catch (classnotfoundexception e) {// classnotfoundexception брошен, если класс не найден // из не нулевого загрузчика класса} if (c == null) {// Если все еще не найдено, то вызовите FindClass в порядке // найти класс. long t1 = System.nanotime (); c = findClass (имя); // это определяющий загрузчик класса; Запишите STATS Sun.misc.perfcounter.getParentDelegationTime (). addTime (T1 - T0); sun.misc.perfcounter.getFindClasStime (). AddElapsedTimefrom (T1); sun.misc.perfcounter.getFindClasses (). IGRMET (); }} if (Resolve) {ResolVeclass (c); } return c; }Одним из преимуществ использования родительского делегации для организации загрузчиков классов является безопасность. Если мы сами определим класс строки, мы надеемся заменить этот класс строки реализацией java.lang.string в Java по умолчанию.
Мы поместили файл класса класса String, который мы реализовали в пути ClassPath. Когда мы используем загрузчик класса для загрузки класса строк, который мы реализовали, сначала класс загрузчик будет делегировать запрос в родительский загрузчик, а через слой по слою загрузчик класса начальной загрузки наконец загрузит тип строки в пакет rt.jar, а затем вернет его нам. В этом процессе наш классный загрузчик игнорирует класс строк, который мы разместили в панели класса.
Если родительский механизм делегирования не принят, загружатель системы системы может найти файл класса строк в пути класса и загружать его в программу, что приводит к перезаписи реализации строки в JDK. Следовательно, этот рабочий метод загрузчиков классов гарантирует, что программы Java могут в определенной степени работать безопасно и стабильно.
Загрузчик по контексту потока
Приведенное выше говорится о столько контенте, связанном с загрузчиками класса, но все еще не говорит о сегодняшней теме, погрузчиках по контексту потоков.
На этом этапе мы уже знаем, что Java предоставляет три типовых погрузчика и работает согласованно в соответствии со строгим родительским механизмом делегирования. На первый взгляд, это кажется идеальным, но именно этот строгой родительский механизм делегирования вызывает некоторые ограничения при загрузке классов.
Когда наша более базовая структура должна использовать классы на уровне приложения, мы можем использовать эти классы только в том случае, если этот класс загружается, когда загрузчик класса, используемый нашей текущей структурой, может быть загружен. Другими словами, мы не можем использовать загрузчик класса текущего загрузчика класса. Это ограничение вызвано механизмом родительского делегирования, потому что делегирование запросов на загрузку класса составляет одностороннее.
Хотя случаев не так много, есть такой спрос. Типичная служба JNDI. JNDI предоставляет интерфейс для ресурсов запроса, но конкретная реализация реализована различными производителями. В настоящее время код JNDI загружается загрузчиком класса Bootstrap JVM, но конкретная реализация-это код, отличный от JDK, предоставленного пользователем, поэтому его можно загрузить только системным загрузчиком класса или другим пользовательским загрузчиком класса. Согласно родительскому механизму делегирования, JNDI не может получить реализацию SPI JNDI.
Чтобы решить эту проблему, вводится загрузчик класса контекста потока. SetContextClassloader () java.lang.thread class setContextClassloader () (если не установлен, он будет унаследован от родительского потока по умолчанию. Если программа не установила, она по умолчанию будет по умолчанию на загрузку системы класса). С помощью загрузчика класса Context Treach приложение может передать загрузчик класса, используемый приложением в код, который использует загрузчик класса верхнего уровня через java.lang.thread.setContextClassloader (). Например, вышеуказанная служба JNDI может использовать этот метод для получения загрузчика класса, который может загружать реализации SPI и получить необходимые классы реализации SPI.
Можно видеть, что введение резьбовых погрузчиков на самом деле является разрушением родительского механизма делегирования, но оно обеспечивает гибкость при загрузке класса.
Разрешить сомнения
Вернувшись к началу, чтобы загрузить классы, реализованные пользователями за пределами фреймворта, эти классы не могут быть загружены через загрузчик класса, используемый Framework. Чтобы обойти родительную модель делегирования класса загрузчика, Thread.getContextClassloader () используется для загрузки этих классов.
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.