Родительская модель делегации
Концепция загрузки класса должна рассматриваться как инновация на языке Java. Цель состоит в том, чтобы отделить процесс загрузки класса с виртуальной машины и достичь цели «получить бинарный байтовый поток, описывающий этот класс через полностью квалифицированное имя класса». Модуль кода, который реализует эту функцию, является загрузчиком класса. Основная модель загрузчика класса - известная модель делегирования родителей. Это звучит потрясающе, но логика на самом деле очень проста. Когда нам нужно загрузить класс, мы сначала определяем, был ли класс загружен. Если нет, мы определяем, был ли он загружен родительским погрузчиком. Если мы не назвали наш собственный метод Findclass, чтобы попробовать загрузку. Это базовая модель (удаление кражи изображения нарушение прав изображения):
Это также очень просто для реализации. Ключ - это метод Loadclass класса ClassLoader. Исходный код заключается в следующем:
Защищенный класс <?> LoadClass (String name, Boolean Resolve) Throws classNotFoundException {Synchronized (getClassLoadingLock (name)) {// сначала проверяйте, был ли класс уже загружен класс <?> 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) {class (c); } return c; }}Внезапно я почувствовал, почему я просто бросил исключение напрямую? На самом деле, это потому, что класс ClassLoader - это абстрактный класс. На самом деле, он будет писать подкласс при использовании. Этот метод будет переписан по мере необходимости для завершения процесса загрузки, требуемого бизнесом.
Пользовательский класс загрузчик
При настройке подкласса загрузчика класса есть два общих метода: один из них - переписать метод LoadClass, а другой - переписать метод FindClass. На самом деле, эти два метода по сути одинаковы. В конце концов, LoadClass также вызовет FindClass, но, логически говоря, лучше не изменить внутреннюю логику LoadClass.
Лично я думаю, что лучший способ - просто переписать метод загрузки пользовательских классов в FindClass.
Почему это лучше? Потому что я также говорил ранее, что метод LoadClass-это место для реализации логики модели с распределенной с родителями. Изменение этого метода без разрешения приведет к разрушению модели и легко вызывает проблемы. Следовательно, лучше всего внести мелкие изменения в рамках модели родительской делегирования, чтобы не разрушать исходную стабильную структуру. В то же время это также позволяет избежать необходимости писать дублированный код, делегированный родителями в процессе переписывания метода нагрузки. С точки зрения повторного использования кода, не изменяя этот метод напрямую, всегда является лучшим выбором.
Конечно, было бы иначе, если бы вы сознательно уничтожили модель родительской комиссии.
Уничтожить модель родительской делегации
Зачем разрушать модель родительской делегирования?
На самом деле, в некоторых случаях нам может потребоваться загрузить два разных класса, но, к сожалению, имена этих двух классов точно одинаковы. В настоящее время родительская модель делегации не может соответствовать нашим требованиям. Нам нужно переписать метод LoadClass, чтобы уничтожить родительскую модель делегирования и позволить одному имени класса загружать несколько раз. Конечно, разрушение, упомянутое здесь, является только разрушением в местном смысле.
Но имена классов одинаковы, как JVM может отличить эти два класса? Очевидно, это не приведет к обрушению мировоззрения. На самом деле, классы ограничены не только именами классов в JVM, но и принадлежат загрузчику класса, который их загружает. Классы, загруженные различными загрузчиками класса, на самом деле не влияют друг на друга.
Сделайте эксперимент.
Давайте сначала напишем два класса:
пакет com.mythsman.test; public class hello {public void say () {System.out.println ("Это от Hello v1"); }} пакет com.mythsman.test; public class hello {public void say () {System.out.println ("Это из Hello v2"); }} Два имена класса одинаковы, единственное отличие состоит в том, что реализация методов отличается. Сначала мы компилируемся отдельно, а затем переименованы сгенерированными файлами класса в hello.class.1 и hello.class.2.
Наша цель - создать экземпляры этих двух классов в тестовом классе.
Затем мы создаем новый тестовый класс com.mythsman.test.main и создаем два пользовательских загрузчика класса в основной функции:
ClassLoader ClassLoader1 = New ClassLoader () {@Override Public Class <?> LOADCLASS (String S) Throws ClassNotFoundException {try {if (s.Equals ("com.mythsman.test.hello")) {byte [] classbytes = files.readallystes (paths.get ("/home/myths/testop/hellop/hellop/hellop/hellop/hellop/hellop/hellop/hellop/hellop/hellop/hellop/hill вернуть Decianclass (s, classbytes, 0, classbytes.length); } else {return super.loadclass (s); }} catch (ioException e) {бросить новое classnotfoundexception (s); }}}; ClassLoader ClassLoader2 = new ClassLoader () {@Override Public Class <?> LOADCLASS (String S) Throws ClassNotFoundException {try {if (s.equals ("com.mythsman.test.hello")) {byte [] classbytes = Files.readallbytes (paths.get ("/home/myths/desktop/test/hello.class.2")); вернуть Decianclass (s, classbytes, 0, classbytes.length); } else {return super.loadclass (s); }} catch (ioException e) {бросить новое classnotfoundexception (s); }}};
Цель этих двух загрузчиков класса состоит в том, чтобы связать два разных байткода класса Hello отдельно. Нам нужно прочитать файл Bytecode и загрузить его в класс с помощью метода Decipenclass. Обратите внимание, что мы перегружаем метод нагрузки. Если мы перегружаем метод FindClass, то из -за механизма обработки перенесенного делегата метода LoadClass метод FindClass второго класса загрузчика не будет вызван.
Итак, как мы генерируем экземпляры? Очевидно, мы не можем напрямую ссылаться на имена классов (конфликты имени), поэтому мы можем только использовать отражение:
Object hellov1 = classloader1.loadClass ("com.mythsman.test.hello"). Newinstance (); объект hellov2 = classloader2.loadclass ("com.mythsman.test.hello"). newinstance (); hellov1.getClass (). getMethod («Say»). invoke (hellov1); hellov2.getClass (). getMethod («скажи». invoke (hellov2); Выход:
Это от Hello v1this от Hello v2
Хорошо, даже если вы завершили две нагрузки, есть еще несколько пунктов, на которые можно обратить внимание.
Каковы отношения между двумя классами
Очевидно, что эти два класса не один класс, но их имена одинаковы. Итак, каковы результаты операторов, как Isinstance of:
System.out.println ("class:"+hellov1.getClass ()); System.out.println ("class:"+hellov2.getClass ()); System.out.println ("есть hcode: "+hellov2.getClass (). hashcode ()); System.out.println (" classloader: "+hellov1.getClass (). getClassloader ()); System.out.println (" ClassLoader: "+hellov2.getClass (). getClassloder ()); Выход:
Класс: класс com.mythsman.test.helloclass: class com.mythsman.test.hellohashcode: 1581781576hashcode: 1725154839classloder: com.mythsman.test.maindn144.44
Их имена классов действительно одинаковы, но хешкоды класса различны, что означает, что эти два по сути не одинаковые классы, и их классные погрузчики также различны (на самом деле, они являются двумя внутренними классами).
Какова взаимосвязь между этими двумя погрузчиками класса и трехслойным загрузчиком класса системы?
Возьмите первый пользовательский загрузчик класса в качестве примера:
System.out.println (classLoader1.getParent (). GetParent (). GetParent ()); System.out.println (classLoader1.getParent (). GetParent ()); System.out.println (classlo Ader1.getParent ()); System.out.println (classLoader1.getParent ()); System.out.println (classLoader1); System.out.println (classloader.getSystemClassLoader ());
Выход:
nullsun.misc.launcher $ [email protected] $ [email protected] $ [email protected] $ appclassloger@18b4aac2
Конечно, упомянутые здесь отношения родителей и родителей-это не наследство, а комбинированные отношения. Дочерний класс -загрузчик сохраняет ссылку (родитель) родительского класса -загрузчика.