В дополнение к «родительскому делегированию» в ClassLoader, эти детали следует понимать подробнее.

Java

Ставь лайк и потом смотри, вырабатывай полезную привычку

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

время загрузки

В дополнение к явному вызову ClassLoader.loadClass для загрузки класса, JVM также будет выполнять операцию загрузки класса в следующих пяти сценариях (ClassLoader.loadClassInternal вызывается JVM).

  1. При создании экземпляра объекта с ключевым словом new, при чтении или установке статического поля класса (за исключением статических полей, измененных с помощью final, и результат был помещен в пул констант во время компиляции), а также при вызове статического метода класса класс когда.
  2. При использовании метода пакета java.lang.reflect для выполнения вызова отражения к классу, если класс не был инициализирован, сначала необходимо запустить его инициализацию.
  3. При инициализации класса, если обнаруживается, что его родительский класс не был инициализирован, ему необходимо сначала инициировать инициализацию своего родительского класса.
  4. Когда виртуальная машина запускается, пользователю необходимо указать основной класс для выполнения (класс, содержащий метод main()), и виртуальная машина сначала инициализирует этот основной класс.
  5. При использовании поддержки динамического языка JDK 1.7, если последним результатом синтаксического анализа экземпляра java.lang.invoke.MethodHandle является дескриптор метода REF_getStatic, REF_putStatic и REF_invokeStatic, а класс, соответствующий этому дескриптору метода, не был инициализирован, его нужно сначала запустить, его инициализацию.

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

Например, в следующем случае это не активная ссылка, и класс не будет загружен.

public class NotInitialization {
    public static void main(String[] args) {
        //根据场景1,读取的是常量,不会造成类的初始化
        System.out.println(ConstClass.HELLOWORLD);
    }
}
class ConstClass{

    static final String HELLOWORLD = "hello world";

    static {
        System.out.println("ConstClass init!");
    }
}

Class.forName

Загрузка класса может осуществляться в виде Class.forName, но здесь используется загрузчик класса вызывающего, то есть загрузчик класса того класса, который инициирует вызов метода Class.forName

public static Class<?> forName(String className)
                throws ClassNotFoundException {
    //获取caller Class
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

«Транзитивность» загрузчиков классов

Во-первых, давайте поговорим о двух понятиях.определение загрузчикаа такжеНачальный загрузчик

Существует существующий ClassLoader A, если класс X напрямую определяется A (вместо делегирования родительского classLoader для загрузки в A), то ClassLoader A называется классом X.определить загрузчик, также называемый ClassLoader A, определяет класс X

Когда ClassLoader поручает родительскому classLoader загрузить класс (loadClass), то classLoader для loadClass и родительский classLoader для defineClass не совпадают.

Существует ClassLoader B, который загружает класс Y с помощью метода loadClass ClassLoader B. Независимо от того, определяет ли ClassLoader B непосредственно класс Y или делегирует родительский classLoader для определения класса Y, тогда ClassLoader B относится к классу Y.загрузчик начального класса.

Просто если класс Y загружается непосредственно ClassLoader B, то класс Yопределить загрузчика такженачальный загрузчикОба являются ClassLoader B; если он загружается родительским классом classLoader C, тоопределить загрузчикэто ClassLoader C, иначальный загрузчикэто ClassLoader B

Как показано на рисунке ниже, для загруженного класса X используется ClassLoader A.начальный загрузчик, и фактически ClassLoader A делегирует ClassLoader B для завершения определения, поэтому ClassLoader Bопределить загрузчик image.png

Класс, загруженный в виде Class.forName, фактически использует вызывающую программу.определить загрузчик

Хоть этот абзац и похож на скороговорку... но он же и ключ к ClassLoader, а характеристики передачи ClassLoader очень важны


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

  • Когда JVM интерпретирует класс, на самом деле"ленивая загрузка", указанный класс будет проанализирован после того, как строка интерпретации и выполнения встретит ссылку; например, если класс вызывается в теле метода, он будет загружен только при выполнении этой строки.
  • Для того же экземпляра ClassLoader будет использоваться класс, который не был загружен в текущем классе.Инициировать загрузчик определений ссылочного класса (вместо системного ClassLoader)загрузить
  • Если ClassLoader уже загрузил определенный класс, он не будет загружен снова (даже loadClass не будет вызываться); например, класс A ссылается на класс X и класс B, а класс B также ссылается на класс X, затем выполнить, когда класс B , операция loadClass(X) больше не будет выполняться

В соответствии с приведенными выше характеристиками относительно просто настроить ClassLoader, например реализацию ClassLoader Executable Jar (Исполняемый Jar), ​​предоставляемую Spring Boot:

Просто создайте ClassLoader (org.springframework.boot.loader.JarLauncher), отвечающий за загрузку пакета jar в пакете jar, а затем загрузите Main-Class в нашем коде через ClassLoader в классе ввода, чтобы загрузка могла быть done Пакет jar в пакете jar:

public void run() throws Exception {
    //通过前面设置的“负载加载jar包内jar包的ClassLoader”,去加载我们程序中的main类
    Class<?> mainClass = Thread.currentThread().getContextClassLoader()
        .loadClass(this.mainClassName);
    Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
    mainMethod.invoke(null, new Object[] { this.args });
}

Ссылаться на

Нелегко быть оригинальным, и несанкционированная перепечатка запрещена. Если моя статья полезна для вас, пожалуйста, поставьте лайк/добавьте в избранное/подпишитесь, чтобы поддержать и поддержать ее ❤❤❤❤❤❤