Какова связь между ModuleLayer и ClassLoader? - PullRequest
2 голосов
/ 13 апреля 2020

Давайте рассмотрим следующую ситуацию. Есть три слоя.

BootLayer (moduleA)
     |
     |_______Child1(moduleB)
                |
                |__________ Child2(moduleC)

Child1 - дочерний элемент BootLayer, Child2 - дочерний объект Child1.

Child1, и Child2 был создан с помощью того же кода:

ClassLoader parentClassLoader = ClassLoader.getSystemClassLoader();
ModuleLayer layer = parentLayer.defineModulesWithOneLoader(cf, parentClassLoader);

Как видите, загрузчиком родительского класса обоих дочерних слоев является SystemClassLoader. Тем не менее, moduleB может использовать классы moduleA, но moduleC может использовать классы moduleA и moduleB.

Я новичок в загрузке классов, но я читал о parent-first delegation model. Однако, если для обоих дочерних слоев используется SystemClassLoader, то почему они видят классы из других слоев? Кто-нибудь может объяснить?

Ответы [ 2 ]

1 голос
/ 15 апреля 2020

Ответ был дан Аланом Бейтманом в списке рассылки jigsaw-dev и размещен здесь.

Слои модуля - это продвинутая тема c. ClassLoaders также являются продвинутыми topi c. При работе со слоями модулей и использовании методов defineModulesWithXXX для создания слоев модулей вам больше не нужно слишком беспокоиться о загрузчиках классов. Они по-прежнему используются для загрузки классов, но в основном они находятся в фоновом режиме (а не на вашем лице).

Вам также не нужно слишком беспокоиться о «родительском загрузчике классов», который вы указываете для defineOneWithOneLoader метод. Он не используется при загрузке классов из модулей, это только когда для случаев, когда код в moduleB или moduleC пытается загрузить класс, который не находится в модуле, возможно Class.forName("Foo"), где Foo находится на пути к классам , Поэтому, вероятно, лучше всего игнорировать загрузчик родительского класса при запуске.

Документы API объясняют, как делегирование работает с модулями, но, возможно, не совсем понятно, для чего здесь нужно. В вашем примере поддержка L1 - это загрузчик класса для moduleB на дочернем уровне 1, а L2 - это загрузчик класса для moduleC на дочернем уровне 2. Далее предположим, что объявления модуля:

module moduleC {
     requires moduleB;
}

module moduleB {
     exports b;
}

Конфигурация для Child1 очень проста: один moduleB, который читает java.base

Конфигурация для Child2 также очень прост: один moduleC, который читает moduleB и java.base.

Когда создается Child1, он создает L1 и отображает moduleB в L1. Когда код в moduleB пытается разрешить ссылку на класс в своем собственном модуле, он будет загружен L1 (без делегирования). Когда moduleB ссылается на класс в java.base, L1 делегирует boot loader.

Когда создается Child2, он создает L2 и отображает moduleC в L2 , Когда код в moduleC пытается разрешить ссылку на класс в своем собственном модуле, он будет загружен L2 (без делегирования). Когда moduleC ссылается на класс b.*, он будет делегирован L1 для разрешения ссылки. Когда moduleC ссылается на класс в java .base, тогда L2 делегирует загрузчику.

Если вы вытягиваете это, вы должны увидеть, что делегирование загрузчика классов является "прямым делегированием" и точно отражает края в графе читаемости (объект конфигурации).

Надеюсь, этого достаточно для начала работы. Это действительно нуждается в диаграммах и графиках, чтобы объяснить некоторые из этих деталей, вероятно. Как я уже сказал, при работе со слоями модуля вы можете в основном игнорировать детали загрузчика классов.

1 голос
/ 14 апреля 2020

Во-первых, я предполагаю, что родительский слой Child2 - это Child1.


Давайте начнем со спецификации :

Членство в модуле определено в условия выполнения пакетов ( §5.3 ). Программа определяет имена пакетов в каждом модуле и загрузчики классов, которые будут создавать классы и интерфейсы именованных пакетов; затем он определяет пакеты и загрузчики классов для вызова метода defineModules класса ModuleLayer. При вызове defineModules виртуальная машина Java создает новые модули времени выполнения, связанные с пакетами времени выполнения загрузчиков классов.

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

Модуль времени выполнения неявно является частью ровно одного слоя, по семантике defineModules. Однако загрузчик классов может создавать классы в модулях времени выполнения разных слоев, поскольку один и тот же загрузчик классов может быть указан для нескольких вызовов defineModules. Управление доступом регулируется модулем времени выполнения класса, а не загрузчиком классов, который создал класс, или слоями, которые обслуживает загрузчик классов.

Делегирование загрузчика классов не имеет значения.

Есть сходства и различия между загрузчиками классов и слоями. С одной стороны, слой похож на загрузчик классов в том, что каждый может делегировать, соответственно, один или несколько родительских слоев или загрузчиков классов, которые создали соответственно модули или классы в более раннее время. То есть набор модулей, указанных для слоя, может зависеть от модулей, не указанных для уровня, а вместо этого указанных ранее для одного или нескольких родительских слоев. С другой стороны, слой может использоваться для создания новые модули только один раз, тогда как загрузчик классов может быть использован для создания новых классов или интерфейсов в любое время с помощью нескольких вызовов метода defineClass.

(выделение мое)


Этого должно быть достаточно, чтобы ответить на ваш вопрос:

  • Поскольку родительский слой Child1 является загрузочным слоем Модули в Child1 могут зависеть от модулей в загрузочном слое.
  • Поскольку родительским уровнем Child2 является Child1, модули в слое Child2 могут зависеть от модулей в Child1 и загрузочного слоя.
...